//
//  LoginViewController.swift
//  ECH_LA
//
import UIKit
import ObjectMapper
class LoginViewController: UIViewController {
    @IBOutlet weak var accountTF: UITextField!
    @IBOutlet weak var passwordTF: UITextField!
    
    @IBOutlet weak var loginCenter: NSLayoutConstraint!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        accountTF.autocorrectionType = .no
        accountTF.autocapitalizationType = .none
        accountTF.spellCheckingType = .no
        
        passwordTF.autocorrectionType = .no
        passwordTF.autocapitalizationType = .none
        passwordTF.spellCheckingType = .no
//        
//        accountTF.text = "biotest"
//        passwordTF.text = "test@wqaz"
        self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelEdit)))
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    deinit {
        SSLog("deinit")
    }
    
    @objc func cancelEdit() {
        self.view.endEditing(true)
    }
    
    @objc private func keyboardWillChangeFrame(_ notification: NSNotification) {
        loginCenter.constant = -60
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
    }
    
    @objc private func keyboardWillHide(_ notification: NSNotification) {
        loginCenter.constant = 0
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
    }
    
    @IBAction func login(_ sender: Any) {
        guard let account = accountTF.text?.trimmingCharacters(in: .whitespacesAndNewlines), !account.isEmpty else {
            SSHUD.ss_showError(withStatus: "请检查你输入的用户名")
            return
        }
        guard let pas = passwordTF.text?.trimmingCharacters(in: .whitespacesAndNewlines), !pas.isEmpty else {
            SSHUD.ss_showError(withStatus: "请检查你输入的密码")
            return
        }
        login(userName: account, pas: pas)
    }
    
    func login(userName:String, pas: String) {
        
        SSHUD.ss_show(withStatus: "登录中")
        let param = ["username": userName, "password": pas]
        _=SSNetworkManager.shared.postRequest(URLString: kLoginUrl, parameters: param, success: { (dict) in
            SSLog(dict)
            SSHUD.ss_dismiss()
            guard let token = dict["token"] as? String else {
                SSHUD.ss_showError(withStatus: "token错误, 请联系我们")
                return
            }
            if let arrDict = dict["devices"], let devices = Mapper<Device>().mapArray(JSONObject: arrDict) {
                SSDBManager.shared.updateAllDevices(devices: devices)
            }
            UserDefaults.standard.set(token, forKey: "Token")
            let main = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "main")
            UIApplication.shared.windows.first?.rootViewController = main
        }) {error,_ in
            SSHUD.ss_showError(withStatus: error)
        }
    }
}
/
//  User.swift
//  ECH_LA
//
import UIKit
struct User {
    
}
class UserService {
    
    static let shared = UserService()
    func logout() {
        La_dataServer.share.clearAll()
        UserDefaults.standard.set("", forKey: "Token")
        MQTTServer.share.cancelConnect()
        let vc = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "login") as! LoginViewController
        UIApplication.shared.windows.first?.rootViewController = vc
    }
}
/
//  User.swift
//  ECH_LA
//
import UIKit
struct User {
    
}
class UserService {
    
    static let shared = UserService()
    func logout() {
        La_dataServer.share.clearAll()
        UserDefaults.standard.set("", forKey: "Token")
        MQTTServer.share.cancelConnect()
        let vc = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "login") as! LoginViewController
        UIApplication.shared.windows.first?.rootViewController = vc
    }
}
//
//  DeviceTableViewCell.swift
//  ECH_LA
//
import UIKit
protocol DeviceTableViewCellDelegate: AnyObject {
    func deviceDidClickLine(cell: DeviceTableViewCell)
    func deviceDidClickHistory(cell: DeviceTableViewCell)
}
class DeviceTableViewCell: UITableViewCell {
    @IBOutlet weak var bg: UIView! {didSet{
        bg.ss_shadow(radius: 8, opacity: 0.1, color: UIColor.label, offset: CGSize(width: 0, height: 4))
    }}
    @IBOutlet weak var deviceData: UILabel!
    @IBOutlet weak var deviceName: UILabel!
    
    weak var delegate: DeviceTableViewCellDelegate?
    
    var device: (Device, SSDeviceConnectState?, String?)? {didSet {
        guard let device1 = device else {
            return
        }
        
        var isConnect = false
        var connectInfo = "未连接"
        if let connectStatus = device1.1 {
            switch connectStatus {
            case .connected:
                connectInfo = "已连接"
                isConnect = true
            case .unconnect:
                connectInfo = "连接断开"
            case .connecting:
                connectInfo = "连接中"
            case .connectFailed:
                connectInfo = "连接失败"
            case .unbind:
                connectInfo = "未绑定"
            case .noBle:
                connectInfo = "无蓝牙"
            }
            if connectStatus == .connectFailed {
            }
        }
        
        if let lastData = device1.2 {
            deviceData.text = lastData
        } else {
            deviceData.text = "等待数据"
        }
        deviceName.text = device1.0.value + " (\(connectInfo))"
        if isConnect {
            deviceName.textColor = UIColor.green
        } else {
            deviceName.textColor = UIColor.red
        }
    }}
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    @IBAction func lineClick(_ sender: Any) {
        delegate?.deviceDidClickLine(cell: self)
    }
    
    @IBAction func dataHistoryClick(_ sender: Any) {
        delegate?.deviceDidClickHistory(cell: self)
    }
}
//
//  ViewController.swift
//  ECH_LA
//
import UIKit
import SafariServices
class MainViewController: UIViewController {
    
    @IBOutlet weak var mqttConnectInfo: UILabel!
    @IBOutlet weak var dataTableView: UITableView! {didSet{
    }}
    
    @IBOutlet weak var versionInfo: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "unknown"
        let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
        versionInfo.text = "App version:\(version) build \(appBuild)" + "\nSystem version:\(UIDevice.ss_systemVersion())" + " Phone: \(UIDevice.ss_iphoneType())"
        _=LocationManager.share
        _=ECHDeviceManager.instance
        _=La_dataServer.share
        NotificationCenter.default.addObserver(self, selector: #selector(deviceRefresh(_:)), name: kDeviceDeviceConnectStateNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(deviceDataRefresh(_:)), name: kDeviceDeviceDidGetCurrentDataNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(mqttRefresh(noti:)), name: MQTTServer.mqttConnectStatusNotification, object: nil)
        MQTTServer.share.checkConnect()
        dataTableView.mj_header = SSRefreshHeader(refreshingBlock: { [weak self] in
            self?.checkBindDevices(isReload: true)
        })
        checkBindDevices(isReload: false)
    }
    
    deinit {
        SSLog("deinit")
    }
    
    func checkBindDevices(isReload: Bool) {
        if isReload { SSHUD.ss_show(withStatus: "获取设备") }
        DeviceService().checkDevices { devices in
            if isReload { SSHUD.ss_dismiss() }
            self.dataTableView.mj_header?.endRefreshing()
            self.dataTableView.reloadData()
        } fail: { error in
            if isReload { SSHUD.ss_showError(withStatus: error) }
            self.dataTableView.mj_header?.endRefreshing()
        }
    }
    
    func refreshLocalDeviceState() {
        ECHDeviceManager.instance.bleIsConnect()
    }
    
    @IBAction func logout(_ sender: Any) {
        let alert = UIAlertController(title: "要退出当前账号吗?", message: "⚠️注意：未上传至服务器的数据将同时被清除!", preferredStyle: UIAlertController.Style.alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (action) in
            UserService.shared.logout()
        }))
        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (action) in
        }))
        present(alert, animated: true, completion: nil)
    }
    
    @IBAction func logFile(_ sender: Any) {
        SSCommon.shareLog(vc: self)
    }
    
    @objc func mqttRefresh(noti: Notification) {
        if let status = noti.object as? Int {
           
            switch status {
            case 0:
                mqttConnectInfo.text = "Mqtt 未连接(请检查您的手机网络)"
                mqttConnectInfo.textColor = UIColor.red
            case 1:
                mqttConnectInfo.text = "Mqtt 连接中"
                mqttConnectInfo.textColor = UIColor.orange
            case 2:
                mqttConnectInfo.text = "Mqtt 已连接"
                mqttConnectInfo.textColor = UIColor.green
            default:
                mqttConnectInfo.text = "Mqtt 未知"
                mqttConnectInfo.textColor = UIColor.gray
            }
        }
        dataTableView.reloadData()
    }
    
    @objc func deviceRefresh(_ noti: Notification) {
        if let data = noti.object as? (String, SSDeviceConnectState) {
           let name = String(data.0)
            DeviceService.share.updateDeviceStatus(name: name, status: data.1)
        }
        dataTableView.reloadData()
    }
    
    @objc func deviceDataRefresh(_ noti: Notification) {
        if let data = noti.object as? (String, String) {
            let name = String(data.0)
            DeviceService.share.updateDeviceLastData(name: name, data: data.1)
        }
        dataTableView.reloadData()
    }
    
//    @IBAction func clearAll(_ sender: Any) {
//        let alert = UIAlertController(title: "清除所有保存的测试数据?", message: "", preferredStyle: UIAlertController.Style.alert)
//        alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (action) in
//            La_dataServer.share.clearAll()
//        }))
//        alert.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (action) in
//        }))
//        present(alert, animated: true, completion: nil)
//    }
}
extension MainViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return DeviceService.share.devices.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let device = DeviceService.share.devices[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "deviceCell", for: indexPath) as! DeviceTableViewCell
        cell.delegate = self
        cell.device = device
        return cell
    }
}
extension MainViewController: DeviceTableViewCellDelegate {
    
    func deviceDidClickHistory(cell: DeviceTableViewCell) {
        guard let indexPath = dataTableView.indexPath(for: cell) else {
            return
        }
        let device = DeviceService.share.devices[indexPath.row]
        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "dataVC") as! DataViewController
        vc.name = device.0.value
        present(vc, animated: true, completion: nil)
    }
    
    func deviceDidClickLine(cell: DeviceTableViewCell) {
        
        guard let indexPath = dataTableView.indexPath(for: cell) else {
            return
        }
        let device = DeviceService.share.devices[indexPath.row]
        guard let token = UserDefaults.standard.string(forKey: "Token"), !token.isEmpty else {
            SSHUD.ss_showError(withStatus: "token错误, 请尝试重新登录")
            return
        }
        let url = URL(string: "https://app.bioecare.com/admin/statics/biomreport.html?token=\(token)&device=\(device.0.value)")!
        if UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.open(url)
        } else {
            let vc = SFSafariViewController(url: url)
            self.present(vc, animated: true, completion: nil)
        }
    }
}
//
//  Device.swift
//  ECH_LA
//
//
import Foundation
import WCDBSwift
import ObjectMapper
struct Device: Mappable, TableCodable {
    var value = ""
    
    init?(map: Map) {
    }
    mutating func mapping(map: Map) {
        value <- map["value"]
    }
    
    enum CodingKeys: String, CodingTableKey {
        typealias Root = Device
        case value
        static let objectRelationalMapping = TableBinding(CodingKeys.self)
        static var columnConstraintBindings: [CodingKeys: ColumnConstraintBinding]? {
            return [
                value: ColumnConstraintBinding(isPrimary: true),
            ]
        }
    }
}
class DeviceService {
    
    static let share = DeviceService()
    
    var devices: [(Device, SSDeviceConnectState?, String?)] {
        var deviceDatas = [(Device, SSDeviceConnectState?, String?)]()
        let devices = SSDBManager.shared.getAllDevices()
        for device in devices {
            
            var deviceDataObj:(Device, SSDeviceConnectState?, String?) = (device, nil, nil)
            if let status = deviceState[device.value] {
                deviceDataObj.1 = status
            }
            if let data = deviceData[device.value] {
                deviceDataObj.2 = data
            }
            deviceDatas.append(deviceDataObj)
        }
        
        let soD = deviceDatas.sorted { device1, device2 in
            if let _ = device1.1, device2.1 == nil {
                return true
            }
            if let d1C = device1.1, let d2C = device2.1, d1C == .connected, d2C != .connected{
                return true
            }
            return false
        }
        
        return soD
    }
    
    var deviceState:[String: SSDeviceConnectState] = [:]
    var deviceData:[String: String] = [:]
    func updateDeviceStatus(name: String, status: SSDeviceConnectState) {
        deviceState[name] = status
    }
    func updateDeviceLastData(name: String, data: String)  {
        deviceData[name] = data
    }
    func checkDevices(complete: @escaping(([Device])->()), fail: @escaping((String)->())) {
        let param = ["token": UserDefaults.standard.string(forKey: "Token") ?? ""]
        _=SSNetworkManager.shared.postRequest(URLString: kGetDevices, parameters: param, requestType: .json, success: { (dict) in
            SSLog(dict)
            
            guard let device = dict["devices"] else {
                fail("获取设备失败")
                return
            }
            guard let devices = Mapper<Device>().mapArray(JSONObject: device) else {
                fail("获取设备失败")
                return
            }
            SSDBManager.shared.updateAllDevices(devices: devices)
            complete(devices)
        }) {error,_ in
            fail(error)
        }
    }
}
//
//  DataTableViewCell.swift
//  ECH_LA
//
import UIKit
class DataTableViewCell: UITableViewCell {
    @IBOutlet weak var status: UIImageView!
    @IBOutlet weak var name: UILabel!
    @IBOutlet weak var data1: UILabel!
    @IBOutlet weak var data2: UILabel!
    @IBOutlet weak var data3: UILabel!
    @IBOutlet weak var data4: UILabel!
    
    var data: La_data? {
        didSet {
            if let data = data {
                name.text = data.date.ss_str()
                data1.text = data.data1
                data2.text = data.data2
                data3.text = data.data3
                data4.text = data.data4
                if data.uploadStatus == 0 {
                    status.image = UIImage(named: "unknown")
                } else if data.uploadStatus == 1 {
                    status.image = UIImage(named: "uploading")
                } else if data.uploadStatus == 2 {
                    status.image = UIImage(named: "fail")
                } else if data.uploadStatus == 3 {
                    status.image = UIImage(named: "success")
                }
            }
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    }
}
//
//  DataViewController.swift
//  ECH_LA
//
import UIKit
import xlsxwriter
class DataViewController: UIViewController {
    
    var name = ""
    
    @IBOutlet weak var tableView: UITableView!
    
    var dataSource:[La_data] = []
    
    @IBOutlet weak var deviceName: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        loadData()
        deviceName.text = name
    }
    
    @IBAction func close(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
    
    @IBAction func share(_ sender: Any) {
        exportAll()
    }
    
    func loadData() {
        dataSource = La_dataServer.share.getDeviceData(name: name)
        tableView.reloadData()
    }
    
    func exportAll() {
        let fileManager = FileManager.default
        let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last ?? ""
        let filePath = documentPath + "/\(name).xlsx"
        if !fileManager.fileExists(atPath: filePath) {
            fileManager.createFile(atPath: filePath, contents: nil, attributes: nil)
        }
        let workbook = workbook_new(filePath)
        let worksheet = workbook_add_worksheet(workbook, nil)
        worksheet_set_column(worksheet, 0, 0, 30, nil);
        worksheet_set_column(worksheet, 0, 1, 50, nil);
        worksheet_set_column(worksheet, 0, 2, 15, nil);
        worksheet_set_column(worksheet, 0, 3, 15, nil);
        worksheet_set_column(worksheet, 0, 4, 15, nil);
        worksheet_set_column(worksheet, 0, 5, 15, nil);
        worksheet_set_column(worksheet, 0, 6, 15, nil);
        worksheet_write_string(worksheet, 0, 0, "设备", nil);
        worksheet_write_string(worksheet, 0, 1, "时间", nil);
        worksheet_write_string(worksheet, 0, 2, "sensor", nil);
        worksheet_write_string(worksheet, 0, 3, "7", nil);
        worksheet_write_string(worksheet, 0, 4, "5", nil);
        worksheet_write_string(worksheet, 0, 5, "0", nil);
        worksheet_write_string(worksheet, 0, 6, "上传状态", nil);
        for index in dataSource.indices {
            let model = dataSource[index]
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 0, model.name, nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 1, model.date.ss_str(), nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 2, model.data1, nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 3, model.data2, nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 4, model.data3, nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 5, model.data4, nil);
            worksheet_write_string(worksheet, lxw_row_t(index + 1), 6, "\(model.uploadStatus)", nil);
        }
        workbook_close(workbook);
        let fileUrl = URL(fileURLWithPath: filePath)
        if let _ = try? Data(contentsOf: fileUrl) {
            let activityVC = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
            //activityVC就是你要弹出的UIActivityViewController
            let popover = activityVC.popoverPresentationController
            //从该View中弹出UIActivityViewController
            popover?.sourceView = self.view
            popover?.sourceRect = CGRect(x: 0, y:Int(UIScreen.main.bounds.size.height), width: Int(UIScreen.main.bounds.size.width), height: 60)
            popover?.permittedArrowDirections = UIPopoverArrowDirection.any
            present(activityVC, animated: true, completion: nil)
        }
    }
}
extension DataViewController: UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DataTableViewCell") as! DataTableViewCell
        cell.data = dataSource[indexPath.row]
        return cell
    }
    
}
//
//  Iot.swift
//  ScooterOperation
import Foundation
import WCDBSwift
struct La_data: TableCodable {
    var name = ""
    var mac = ""
    var date: Date = Date()
    var data1 = ""
    var data2 = ""
    var data3 = ""
    var data4 = ""
    var uploadStatus = 0 // 0 未知 1 正在上传 2 上传失败 3 上传成功
    
    enum CodingKeys: String, CodingTableKey {
        
        typealias Root = La_data
        
        case name
        case mac
        case date
        case data1
        case data2
        case data3
        case data4
        case uploadStatus
        static let objectRelationalMapping = TableBinding(CodingKeys.self)
        static var columnConstraintBindings: [CodingKeys: ColumnConstraintBinding]? {
            return [
                date: ColumnConstraintBinding(isPrimary: true),
            ]
        }
    }
}
class La_dataServer {
    
    static let userSmsSendSucessNotification = Notification.Name("kUserSmsSendSucessNotification")
    static let share = La_dataServer()
    private var checkDataTimer: Timer?
    init() {
        clearUploading()
        timerCheckDataUploaded()
    }
    
    func timerCheckDataUploaded() {
        checkDataTimer?.invalidate()
        checkDataTimer = nil
        checkDataTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(uploadNotUploadedData), userInfo: nil, repeats: true)
        RunLoop.main.add(checkDataTimer!, forMode: .common)
    }
    
    @objc func uploadNotUploadedData() {
        
//        var dataModel = La_data()
//        dataModel.name = "BECGMCARE_CF3D4E"
//        dataModel.mac = ""
//        dataModel.data1 = "700"
//        dataModel.data2 = "0"
//        dataModel.data3 = "0"
//        dataModel.data4 = "0"
//        dataModel.uploadStatus = 1
//        SSDBManager.shared.insertData(data: dataModel)
//        MQTTServer.share.sendCurrentData(la: dataModel) { result in
//            SSLog(result)
//            if result {
//                dataModel.uploadStatus = 3
//            } else {
//                dataModel.uploadStatus = 2
//            }
//            SSDBManager.shared.updateDataUploaded(data: dataModel)
//        }
        
        let dataList = SSDBManager.shared.getAllDeviceDataNotUploaded()
        var dic: [String: [La_data]] = [:]
        for data in dataList {
            let key = data.name
            dic[key] = [data] + (dic[key] ?? [])
        }
        
        let keys = dic.keys
        for key in keys {
            if let values = dic[key] {
                MQTTServer.share.sendData(la: values) { result in
                    if result {
                        let dataArr = values.map { mode -> La_data in
                            var new = mode
                            new.uploadStatus = 3
                            return new
                        }
                        SSDBManager.shared.updateHistoryDataUploaded(data: dataArr)
                    }
                }
            }
        }
    }
    
    func clearUploading() {
        let uploading = SSDBManager.shared.getUpdateDataUploading()
        let dataArr = uploading.map { mode -> La_data in
            var new = mode
            new.uploadStatus = 2
            return new
        }
        SSDBManager.shared.updateHistoryDataUploaded(data: dataArr)
    }
    
    func clearAll() {
        SSDBManager.shared.clearAll()
    }
    
    func insertCurrentDeviceData(name: String, mac: String, data: String) {
        
        let arr = data.components(separatedBy: ":")
        if arr.count >= 3 {
            var dataModel = La_data()
            dataModel.name = name
            dataModel.mac = mac
            dataModel.data1 = arr[0]
            dataModel.data2 = arr[1]
            dataModel.data3 = arr[2]
            dataModel.data4 = arr[3]
            dataModel.uploadStatus = 1
            SSDBManager.shared.insertData(data: dataModel)
            MQTTServer.share.sendCurrentData(la: dataModel) { result in
                if result {
                    dataModel.uploadStatus = 3
                } else {
                    dataModel.uploadStatus = 2
                }
                SSDBManager.shared.updateDataUploaded(data: dataModel)
            }
        }
    }
    
    func insertHistoryData(name: String, mac: String, data: [Int]) {
        
        var dataModeArr = [La_data]()
        let date = Date()
        let data1 = Array(data.reversed())
        for index in data1.indices {
            var dataModel = La_data()
            let sec = -(index * 60)
            dataModel.date = date.addingTimeInterval(TimeInterval(sec))
            dataModel.name = name
            dataModel.mac = mac
            dataModel.data1 = "\(data1[index])"
            dataModel.data2 = "0"
            dataModel.data3 = "0"
            dataModel.data4 = "0"
            dataModel.uploadStatus = 1
            dataModeArr.append(dataModel)
        }
        if !dataModeArr.isEmpty {
            SSDBManager.shared.insertHistoryData(data: dataModeArr)
            MQTTServer.share.sendData(la: dataModeArr) { result in
                let dataArr = dataModeArr.map { mode -> La_data in
                    var new = mode
                    if result {
                        new.uploadStatus = 3
                    } else {
                        new.uploadStatus = 2
                    }
                    return new
                }
                SSDBManager.shared.updateHistoryDataUploaded(data: dataArr)
            }
        }
    }
    
    func getDeviceData(name: String) -> [La_data] {
        return SSDBManager.shared.getDeviceData(name: name)
    }
}
//
//  User.swift
//  ECH_LA
//
import UIKit
struct User {
    
}
class UserService {
    
    static let shared = UserService()
    func logout() {
        La_dataServer.share.clearAll()
        UserDefaults.standard.set("", forKey: "Token")
        MQTTServer.share.cancelConnect()
        let vc = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "login") as! LoginViewController
        UIApplication.shared.windows.first?.rootViewController = vc
    }
}
//
//  RootViewController.swift
//  ECH_LA
//
import UIKit
class RootViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.red
        tokenJudge()
    }
    
    /// 判断是否有token
    private func tokenJudge() {
        if let token = UserDefaults.standard.string(forKey: "Token"), !token.isEmpty {
            let main = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "main")
            UIApplication.shared.windows.first?.rootViewController = main
        } else {
            let vc = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: "login") as! LoginViewController
            UIApplication.shared.windows.first?.rootViewController = vc
        }
    }
}
//
//  MQTTSwift.swift
//  MQTTSwift
//
//
import MQTTClient
import Foundation
import ObjectMapper
struct MQTTMessage: Mappable {
    var cmd: String = ""
    var type: String = ""
    var num: String = ""
    var mon: String = ""
    var noId: String = "" // 消息id
    var useId: String = "" // 行程id
    init?(map: Map) {}
    mutating func mapping(map: Map) {
        cmd     <- map["cmd"]
        type    <- map["type"]
        num     <- map["num"]
        mon     <- map["mon"]
        noId    <- map["noId"]
        useId    <- map["useId"]
    }
}
class MQTTServer: NSObject  {
    
    static let mqttConnectStatusNotification = Notification.Name("mqttConnectStatusNotification")
    static let mqttDidReceiveMessageNotification = Notification.Name("mqttDidReceiveMessageNotification")
    static let share = MQTTServer()
    private var session:MQTTSession?
    
    var isLostConnect = true
    
    override init() {
        MQTTLog.setLogLevel(.debug)
    }
    
    func connect() {
        
        SSLog("MQTT: 开始连接")
        // 设置地址和端口号
        let transport = MQTTCFSocketTransport()
        transport.host = "mqtt.bioecare.com"
        transport.port = 1883
        // 设置mqtt账号和密码
        let session = MQTTSession()
        self.session = session
        session?.transport = transport
        session?.delegate = self
        session?.userName = "admin"
        session?.password = "public"
        NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 1)
        DispatchQueue.global().async {
            session?.connectAndWaitTimeout(3)
        }
        
//        SSLog(UserDefaults.standard.value(forKey: kMQTTScooterTopic))
    }
    
    func checkConnect() {
        if let status = session?.status, status == .connected {
            NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 2)
        } else {
            connect()
        }
    }
    
    func cancelConnect()  {
        if let status = session?.status, status == .connected {
            session?.disconnect()
        }
    }
}
extension MQTTServer {
    func sendCurrentData(la: La_data, complete:@escaping ((Bool)->())) {
        sendData(la: [la]) { result  in
            SSLog(result)
            complete(result)
        }
    }
    
    func sendData(la: [La_data], complete:@escaping ((Bool)->())) {
        guard let la1 = la.first else {
            return
        }
        var jsonData = [String: Any]()
        var dataArr = [[String: Any]]()
        for model in la {
            var dataDic = [String: Any]()
            dataDic["sensor"] = model.data1
            dataDic["ref"] = model.data2
            dataDic["battery"] = model.data3
            dataDic["ntc"] = model.data4
            dataDic["dataTime"] = Int(model.date.timeIntervalSince1970)
            dataArr.append(dataDic)
        }
        jsonData["deviceName"] = la1.name
        jsonData["deviceData"] = dataArr
        guard let data = try? JSONSerialization.data(withJSONObject: jsonData, options: JSONSerialization.WritingOptions.init(rawValue:0)) else {
            return
        }
        let json = String(data: data, encoding: .utf8)
        SSLog(json)
        SSCommon.saveLog(tag: "MQTT", info: json ?? "json错误")
        if let session = session, !isLostConnect {
            session.publishData(data, onTopic: "bioecare_cgm_data", retain: false, qos: .exactlyOnce, publishHandler: { error in
                SSLog(error)
                if let _ = error {
                    complete(false)
                } else {
                    complete(true)
                }
            })
        } else {
            complete(false)
        }
    }
}
extension MQTTServer: MQTTSessionDelegate {
    
    func connected(_ session: MQTTSession!) {
        SSLog("MQTT connected: success")
        NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 2)
        if isLostConnect {
            SSCommon.saveLog(tag: "MQTT", info: "Connected")
        }
        La_dataServer.share.clearUploading()
        isLostConnect = false
//        if let topic = UserDefaults.standard.value(forKey: kMQTTScooterTopic) as? String, topic.count > 0 {
//            
//            DispatchQueue.main.async {
//                session.subscribe(toTopic: "bioecare_cgm_data", at: MQTTQosLevel.atMostOnce, subscribeHandler: { (error, num) in
//                    SSLog(error)
////                    SSCommon.saveLog(tag: "MQTT", info: "Topic Subscribe Success")
//                })
//            }
//        }
//        La_dataServer.share.insertCurrentDeviceData(name: "BECGMCARE_CF3D4E", mac: "", data: "700:0:3123:0")
//        La_dataServer.share.insertHistoryData(name: "BECGMCARE_CF3D4E", mac: "", data: [701,702,702,701,703,704,705,707,702])
    }
    func connectionClosed(_ session: MQTTSession!) {
        NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 0)
        SSLog("MQTT connected: closed")
        SSCommon.saveLog(tag: "MQTT", info: "Closed")
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
//            if let topic = UserDefaults.standard.value(forKey: kMQTTScooterTopic) as? String, topic.count > 0 {
                DispatchQueue.global().async {
                    session?.connectAndWaitTimeout(3.5)
                }
//            }
        }
    }
    func connectionError(_ session: MQTTSession!, error: Error!) {
        NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 0)
        if !isLostConnect {
//            SSCommon.saveLog(tag: "MQTT", info: "Error:\(error.localizedDescription)")
        }
        isLostConnect = true
        SSLog("MQTT connected: error \(error.localizedDescription)")
    }
    func connectionRefused(_ session: MQTTSession!, error: Error!) {
        NotificationCenter.default.post(name: MQTTServer.mqttConnectStatusNotification, object: 0)
        if !isLostConnect {
//            SSCommon.saveLog(tag: "MQTT", info: "Refused:\(error.localizedDescription)")
        }
        isLostConnect = true
        SSLog("MQTT connected: refused \(error.localizedDescription)")
    }
    func newMessage(_ session: MQTTSession!, data: Data!, onTopic topic: String!, qos: MQTTQosLevel, retained: Bool, mid: UInt32) {
        let info = "Message topic:\(String(describing: topic))" +  " qos:\(qos.rawValue)" +  " message:\(String(data: data, encoding: .utf8) ?? "")" + " retained:\(retained)" + " mid:\(mid)"
        SSLog(info)
//        SSCommon.saveLog(tag: "MQTT", info: info)
//        if let topic1 = UserDefaults.standard.value(forKey: kMQTTScooterTopic) as? String, topic1 == topic {
//
//            let dic = Data.dataToJSON(data: data)
//            if let message = Mapper<MQTTMessage>().map(JSONObject: dic) {
//
//                if message.cmd == "STARTOK" { // 开始行程成功
//                    NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveStartNotification, object: message.useId)
//                }
//
//                if message.cmd == "ENDOK" { // 结束行程成功
//                    UserDefaults.standard.set("", forKey: kEndMqttOrderNum)
//                    NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveEndNotification, object: message.useId)
//                }
//
//                if message.cmd == "UNLOCKOK" { // 解锁成功
//                    NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveUnlockNotification, object: message.useId)
//                }
//
//                if message.cmd == "LOCKOK" { // 锁定成功
//                    NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveLockNotification, object: message.useId)
//                }
//
//                //MQTT topic:APP412493f550bb2509ee07aa68f3f45ae9message:{ "cmd":"OK","type":"re","num":"RYDE201910181019102","mon":"100"}
//                if message.type == "re" { // 充值
//                    if message.cmd == "OK" {
//                        NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveRechargeSuccessNotification, object: message.num)
//                    } else {
//                        NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveRechargeFailNotification, object: message.num)
//                    }
//                }
//
//                if message.type == "order" { // 订单
//                    if message.cmd == "OK" {
//
//                        UserDefaults.standard.set(message.num, forKey: kEndMqttOrderNum)
//                        NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveOrderSuccessNotification, object: message.num)
//                    } else {
//                        NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveOrderFailNotification, object: nil)
//                    }
//                }
//
//                // 上报 MQTT topic:APP412493f550bb2509ee07aa68f3f45ae9message:{ "cmd":"notice","noId":211}
//                if message.cmd == "notice" { // 消息
////                    NotificationCenter.default.post(name: MQTTServer.mqttDidReceiveMessageNotification, object: message.noId)
//
//                    if let user = UserServer.share.user {
//                        MessageServer.share.getAllMessage(token: user.token, cityId: currentAccount.userCityId, isHideHud: true)
//                        UserServer.share.getUserInfo(token: user.token, isLogin: false)
//                    }
//                }
//            }
//        }
    }
}
//
//  SSDeviceInfo.swift
//  BeltPro
//
//
import Foundation
import CoreBluetooth
enum SSDeviceConnectState {
    case connected
    case unconnect
    case connecting
    case connectFailed
    case unbind
    case noBle
}
struct SSDeviceInfoModel {
    
    /// 设备名字
    var device:CBPeripheral?
    /// 设备名字
    var deviceName:String?
    /// 设备RSSI强度
    var deviceRSSI = -100
    /// 设备唯一标识
    var deviceMac:String?
    /// 设备连接状态
    var deviceState: SSDeviceConnectState = .unconnect
}
//
//  ECHDevice.swift
//  BeltConsumer
//
//
import UIKit
import CoreBluetooth
// TODO: - 通知
let kDeviceBindDidSearchedNotification = Notification.Name("kDeviceBindDidSearchedNotification")
let kDeviceDeviceConnectStateNotification = Notification.Name("kDeviceDeviceConnectStateNotification")
let kDeviceDeviceDidGetCurrentDataNotification = Notification.Name("kDeviceDeviceDidGetCurrentDataNotification")
class ECHDeviceManager {
    
    static let instance = ECHDeviceManager()
    /// 蓝牙外设管理者
    public var bleDevice:SSBleDevice! = nil
    /// 蓝牙是否开启
    public var bleOn = false
    public var searchedDevice:[SSDeviceInfoModel] = []
    public var connectedDevice:[SSDeviceInfoModel] = []
    private var checkConnectingTimer: Timer?
    private var uploadTimer: Timer?
    
    private var historyPackageCount = [String: Int] ()
    private var historyData = [String: [Int]] ()
    init() {
        bleDevice = SSBleDevice(delegate: self)
        bleDevice.scanDelegate = self
        timerCheckDeviceConnect()
    }
    
    /// 这里绑定成功的回调后再进行蓝牙设备的扫描
    func timerCheckDeviceConnect() {
        checkConnectingTimer?.invalidate()
        checkConnectingTimer = nil
        checkConnectingTimer = Timer.scheduledTimer(timeInterval: 8, target: self, selector: #selector(bleIsConnect), userInfo: nil, repeats: true)
        RunLoop.main.add(checkConnectingTimer!, forMode: .common)
    }
}
/// 用户操作
extension ECHDeviceManager {
    
    func startSearchingDeviceToBind() {
        searchedDevice.removeAll()
        bleDevice.startScan()
        SSLog("正在搜索附近设备")
    }
    
    @objc func bleIsConnect() {
        for per in bleDevice.peripherals {
            var isBind = false
            let devices = DeviceService.share.devices.map { device in
                device.0.value
            }
            for name in devices {
                if let na = per.name, na == name {
                    isBind = true
                }
            }
            if !isBind {
                bleDevice.cancelConnect(peripheral: per)
            } else {
                if per.state != .connected {
                    bleDevice.connect(peripheral: per)
                }
            }
        }
        searchedDevice.removeAll()
        bleDevice.startScan()
    }
    
    func unbindDevice(name: String) {
        
        for per in bleDevice.peripherals {
            if let na = per.name, na == name {
                bleDevice.cancelConnect(peripheral: per)
            }
        }
    }
}
// 包含获取模式及模式详情的网络请求及数据存储
extension ECHDeviceManager {
    
    /// 检查用户是否有上传的操作记录
    @objc func uploadUse() {
        // 上传的操作记录
    }
}
extension ECHDeviceManager: SSBleDeviceDelegate {
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice dataCount: Int, packageCount: Int, peripheral: CBPeripheral) {
        let name = peripheral.name ?? "unknown"
        historyPackageCount[name] = packageCount
        historyData[name] = []
        SSHUD.ss_showMessage(withStatus: "\(name)\n正在传输历史数据\n1/\(packageCount)")
        bleDevice.checkPackageData(per: peripheral, name: name, package: 1)
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice data: [Int], packageNumber: Int, peripheral: CBPeripheral) {
        let name = peripheral.name ?? "unknown"
        guard let packageCount = historyPackageCount[name] else {
            return
        }
        SSLog(data)
        if let oldData = historyData[name] {
            historyData[name] = oldData + data
        } else {
            historyData[name] = data
        }
        if packageNumber >= packageCount {
            SSLog("传输结束\(name):\(packageNumber)")
            SSHUD.ss_showMessage(withStatus: "\(name)\n传输历史数据结束")
            bleDevice.checkHistoryEndData(per: peripheral, name: name)
        } else {
            SSLog("传输下一包\(name):\(packageNumber + 1)")
            SSHUD.ss_showMessage(withStatus: "\(name)\n正在传输历史数据\n\(packageNumber + 1)/\(packageCount)")
            bleDevice.checkPackageData(per: peripheral, name: name, package: packageNumber + 1)
        }
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceEnd peripheral: CBPeripheral) {
        let name = peripheral.name ?? "unknown"
        if let historyData = historyData[name] {
            SSLog("准备存储数据：\(historyData)")
            La_dataServer.share.insertHistoryData(name: name, mac: "", data: historyData)
        }
        historyPackageCount[name] = 0
        historyData[name] = []
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceDataError peripheral: CBPeripheral) {
        let name = peripheral.name ?? "unknown"
        historyData[name] = []
        bleDevice.checkHistoryEndData(per: peripheral, name: name)
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceCurrentData data: String, peripheral: CBPeripheral) {
        let name = peripheral.name ?? "unknown"
        La_dataServer.share.insertCurrentDeviceData(name: name, mac: "", data: data)
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: kDeviceDeviceDidGetCurrentDataNotification, object: (name, data), userInfo: nil)
        }
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceVersion version: String) {
        
    }
    
    func bleDeviceDidUpdateState(_ bleDevice: SSBleDevice) {
        switch bleDevice.state {
        case .poweredOff:
            break
        case .poweredOn:
            break
        }
    }
    
    func bleDeviceWillRestoreState(_ bleDevice: SSBleDevice) {
        
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didConnect peripheral: CBPeripheral) {
        SSLog("连接成功")
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: kDeviceDeviceConnectStateNotification, object: (peripheral.name ?? "unknown", SSDeviceConnectState.connected), userInfo: nil)
        }
//        // 检查历史
        bleDevice.checkHistoryData(per: peripheral, name: peripheral.name!)
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        SSLog("连接失败")
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: kDeviceDeviceConnectStateNotification, object: (peripheral.name ?? "unknown", SSDeviceConnectState.unconnect), userInfo: nil)
        }
    }
    
    func bleDevice(_ bleDevice: SSBleDevice, didDisconnectPeripheral peripheral: CBPeripheral) {
        SSLog("断开")
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: kDeviceDeviceConnectStateNotification, object: (peripheral.name ?? "unknown", SSDeviceConnectState.unconnect), userInfo: nil)
        }
    }
}
extension ECHDeviceManager: SSBleDeviceScanDelegate {
    
    func bleDevice(_ bleDevice: SSBleDevice, didDiscover peripheral: CBPeripheral, mac: String, rssi RSSI: NSNumber) {
        
        var device = SSDeviceInfoModel()
        device.device = peripheral
        device.deviceName = peripheral.name ?? ""
        device.deviceRSSI = RSSI.intValue
        device.deviceMac = mac
        device.deviceState = .unconnect
        searchedDevice.append(device)
        searchedDevice.sort { (model1, model2) -> Bool in
            return model1.deviceRSSI > model2.deviceRSSI
        }
        let deviceNames = DeviceService.share.devices.map { device in
            device.0.value
        }
        for name in deviceNames {
            if let na = peripheral.name, na == name {
                self.bleDevice.connect(peripheral: peripheral)
            }
        }
        DispatchQueue.main.async {
            NotificationCenter.default.post(name: kDeviceBindDidSearchedNotification, object: nil, userInfo: nil)
        }
    }
}
//
//  SSBleDevice.swift
//  SSSwiftBleTest
//
//
import UIKit
import CoreBluetooth
/// 蓝牙状态
public enum SSBleState: Int {
    case poweredOn
    case poweredOff
}
/// 设备工作状态
public enum SSDeviceWorkState: Int {
    case working
    case pause
    case ending
}
/// 设备工作状态
public enum SSDeviceConnectingState: Int {
    case unbind
    case searching
    case searchNone
    case connected
}
// MARK: - SSBleDeviceDelegate 方法
protocol SSBleDeviceDelegate: AnyObject {
    /// 蓝牙状态更新
    func bleDeviceDidUpdateState(_ bleDevice: SSBleDevice)
    /// 设备强制重连
    func bleDeviceWillRestoreState(_ bleDevice: SSBleDevice)
    /// 设备连接成功
    func bleDevice(_ bleDevice: SSBleDevice, didConnect peripheral: CBPeripheral)
    /// 设备连接失败
    func bleDevice(_ bleDevice: SSBleDevice, didFailToConnect peripheral: CBPeripheral, error:Error?)
    /// 设备连接断开
    func bleDevice(_ bleDevice: SSBleDevice, didDisconnectPeripheral peripheral: CBPeripheral)
    /// 设备版本号
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceVersion version: String)
    /// 获取到设备当前数据
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceCurrentData data: String, peripheral: CBPeripheral)
    /// 获取到设备历史数据数量
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice dataCount: Int, packageCount: Int, peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice data: [Int], packageNumber: Int, peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceDataError peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceEnd peripheral: CBPeripheral)
}
// Optional
extension SSBleDeviceDelegate {
    
}
protocol SSBleDeviceScanDelegate: AnyObject {
    /// 扫描到蓝牙设备
    func bleDevice(_ bleDevice: SSBleDevice, didDiscover peripheral: CBPeripheral, mac:String, rssi RSSI: NSNumber)
}
class SSBleDevice: NSObject {
    
    /// 服务标识
    private let kServiceUUID = "8653000A-43E6-47B7-9CB0-5FC21D4AE340"
    /// 写服务标识
    private let kWriteUUID = "8653000C-43E6-47B7-9CB0-5FC21D4AE340"
    /// 接收通知标识
    private let kNotiUUID = "8653000B-43E6-47B7-9CB0-5FC21D4AE340"
    /// 代理
    weak var delegate: SSBleDeviceDelegate?
    /// 扫描代理
    weak var scanDelegate: SSBleDeviceScanDelegate?
    /// 蓝牙状态
    public var state: SSBleState = .poweredOff
    
    /// 蓝牙管理者
    private var centralManager: CBCentralManager?
    /// 蓝牙设备
    public var peripherals: [CBPeripheral] = []
    public var writeChars: [String: CBCharacteristic] = [:]
    public var peripheralTokens: [String: Data] = [:]
    /// 初始化
    convenience init(delegate: SSBleDeviceDelegate?, identifier: String? = nil) {
        
        self.init()
        if let identifier = identifier {
            centralManager = CBCentralManager(delegate: self, queue: DispatchQueue(label: "La.ble", attributes: .concurrent), options: [CBCentralManagerOptionRestoreIdentifierKey:identifier,CBCentralManagerOptionShowPowerAlertKey:true])
        }else {
            centralManager = CBCentralManager(delegate: self, queue: DispatchQueue(label: "La.ble", attributes: .concurrent))
        }
        self.delegate = delegate
    }
    
    override init() {
        super.init()
    }
    
    deinit {
        stopScan()
    }
    
    // 根据uuid取到外设
    func retrievePeripheralWithUUID(uuidStrings:[UUID]) -> Bool {
        if let peripherals = centralManager?.retrievePeripherals(withIdentifiers: uuidStrings) {
            self.peripherals.append(contentsOf: peripherals)
            for per in peripherals {
                centralManager?.connect(per, options: nil)
            }
            return true
        }
        return false
    }
    
    /// 开始扫描
    public func startScan() {
        centralManager?.stopScan()
        centralManager?.scanForPeripherals(withServices: nil, options: nil)
    }
    
    /// 停止扫描
    public func stopScan() {
        centralManager?.stopScan()
    }
    
    /// 连接设备
    public func connect(peripheral: CBPeripheral) {
        guard peripheral.state != .connected else {
            return
        }
        SSLog("开始连接" + (peripheral.name ?? "unknown"))
        centralManager?.connect(peripheral, options: nil)
    }
    
    /// 断开设备
    public func cancelConnect(peripheral: CBPeripheral) {
        guard peripheral.state == .disconnected else {
            return
        }
        centralManager?.cancelPeripheralConnection(peripheral)
    }
    
    /// 发送数据
    func sendKeyValue(peripheral: CBPeripheral, data: Data, char: CBCharacteristic) {
        SSLog("发送数据:\(dataToString(data: data))")
        /// 向写服务里面写数据
        peripheral.writeValue(data, for: char, type: .withoutResponse)
    }
    
    /// Data 转 String
    public func dataToString(data: Data) -> String {
        var str = "0x"
        for i in data {
            str += String(format: "%02X ", i&0xff)
        }
        return str
    }
    /// Data 转 String
    public func dataString(data: Data) -> String {
        var str = ""
        for i in data {
            str += String(format: "%02X", i&0xff)
        }
        return str
    }
}
extension SSBleDevice: CBCentralManagerDelegate {
    
    /// 手机蓝牙状态更新
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            SSCommon.saveLog(tag: "Ble", info: "手机蓝牙 开")
            state = .poweredOn
        case .poweredOff:
            SSCommon.saveLog(tag: "Ble", info: "手机蓝牙 关")
            state = .poweredOff
        default:
            state = .poweredOff
        }
        delegate?.bleDeviceDidUpdateState(self)
    }
    
    /// 后台被杀死后重启连接 （但是后台重连后，设备连接着，但没有状态返回，无法显示控制界面，所以通过断开重连的方式来显示界面）
    func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
        
        SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 设备 \(dict)")
        if let array:Array = dict[CBCentralManagerRestoredStatePeripheralsKey] as? Array<CBPeripheral>, array.count > 0 {
            
            peripherals.removeAll()
            if let peripheral:CBPeripheral = array.first {
                if(peripheral.state == CBPeripheralState.connected) {
                    SSLog("restore connect \(peripheral.identifier)");
                    for service in peripheral.services! {
                        if service.uuid.isEqual(CBUUID(string: kServiceUUID)) {
                            for characteristic in service.characteristics! {
                                if characteristic.uuid.isEqual(CBUUID(string: kWriteUUID)) {
                                    // 打开写入服务
                                    if let name = peripheral.name {
                                        writeChars[name] = characteristic
                                    }
                                }
                            }
                        }
                    }
                    // 立即重连后无法断开
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 主动断开")
                        self.centralManager?.cancelPeripheralConnection(peripheral)
                        // 断开后要等几秒钟，低于两秒连不上
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 再次重连")
                            self.centralManager?.connect(peripheral, options: nil)
                        }
                    }
                    delegate?.bleDeviceWillRestoreState(self)
                    return;
                }
                centralManager?.connect(peripheral, options: nil)
            }
        }
    }
    
    /// 扫描到的蓝牙设备
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        
        guard let name = advertisementData["kCBAdvDataLocalName"] as? String else {
            return
        }
        SSLog("发现设备" + name)
        guard (name.hasPrefix("BD_CMG") || name.hasPrefix("BECGM")) else {
            return
        }
        SSCommon.saveLog(tag: "Ble", info: "发现 设备 \(String(describing: peripheral.name)) local name \(name)")
        scanDelegate?.bleDevice(self, didDiscover: peripheral, mac: "", rssi: RSSI)
    }
    
    /// 蓝牙设备连接上
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        // 服务发现并打开之后，我们才认为连接成功
        SSLog("连接成功" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接成功 设备 \(String(describing: peripheral.name))")
        peripherals.append(peripheral)
        peripheral.delegate = self
        peripheral.discoverServices(nil)
        delegate?.bleDevice(self, didConnect: peripheral)
    }
    
    /// 蓝牙连接失败
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        SSLog("连接失败" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接失败 设备 \(String(describing: peripheral.name))")
        peripherals.removeAll { (per) -> Bool in
            per.identifier == peripheral.identifier
        }
        delegate?.bleDevice(self, didFailToConnect: peripheral, error: error)
    }
    
    /// 蓝牙设备断开
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        SSLog("连接断开" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接断开 设备 \(String(describing: peripheral.name))")
        peripherals.removeAll { (per) -> Bool in
            per.identifier == peripheral.identifier
        }
        delegate?.bleDevice(self, didDisconnectPeripheral: peripheral)
    }
}
extension SSBleDevice: CBPeripheralDelegate {
    
    /// 1、发现服务
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        
        for service in peripheral.services! {
            if service.uuid.isEqual(CBUUID(string: kServiceUUID)) {
                // 去发现特征
                peripheral.discoverCharacteristics(nil, for: service)
                break
            }
        }
    }
    
    /// 2、发现服务的特征值
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        
        for characteristic in service.characteristics! {
            if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
                // 打开通知服务
                peripheral.setNotifyValue(true, for: characteristic)
            } else if characteristic.uuid.isEqual(CBUUID(string: kWriteUUID)) {
                // 打开写入服务
                peripheral.setNotifyValue(true, for: characteristic)
                if let name = peripheral.name {
                    writeChars[name] = characteristic
                }
            }
        }
    }
    
    /// 3、确认通知服务打开，读取外设实时数据
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        
        if characteristic.isNotifying {
        
            if peripherals.filter({$0.identifier == peripheral.identifier}).count == 0 {
                peripherals.append(peripheral)
            }
            //TODO: 确认通知服务打开，再加上身份验证，才确认连接成功
            if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
                if let name = peripheral.name {
//                    writeIdentity(per: peripheral, name: name)
//                    checkCurrentData(per: peripheral, name: name)
                    delegate?.bleDevice(self, didConnect: peripheral)
                }
            }
        }
    }
    
    /// 4、接收到数据
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        
        if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
            guard let data = characteristic.value else {
                return
            }
            SSLog("接收数据 【\(peripheral.name ?? "unknown")】：\(dataToString(data: data))")
            transData(peripheral: peripheral, data: data)
        }
    }
}
//
//  SSBleDevice.swift
//  SSSwiftBleTest
//
import UIKit
import CoreBluetooth
/// 蓝牙状态
public enum SSBleState: Int {
    case poweredOn
    case poweredOff
}
/// 设备工作状态
public enum SSDeviceWorkState: Int {
    case working
    case pause
    case ending
}
/// 设备工作状态
public enum SSDeviceConnectingState: Int {
    case unbind
    case searching
    case searchNone
    case connected
}
// MARK: - SSBleDeviceDelegate 方法
protocol SSBleDeviceDelegate: AnyObject {
    /// 蓝牙状态更新
    func bleDeviceDidUpdateState(_ bleDevice: SSBleDevice)
    /// 设备强制重连
    func bleDeviceWillRestoreState(_ bleDevice: SSBleDevice)
    /// 设备连接成功
    func bleDevice(_ bleDevice: SSBleDevice, didConnect peripheral: CBPeripheral)
    /// 设备连接失败
    func bleDevice(_ bleDevice: SSBleDevice, didFailToConnect peripheral: CBPeripheral, error:Error?)
    /// 设备连接断开
    func bleDevice(_ bleDevice: SSBleDevice, didDisconnectPeripheral peripheral: CBPeripheral)
    /// 设备版本号
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceVersion version: String)
    /// 获取到设备当前数据
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceCurrentData data: String, peripheral: CBPeripheral)
    /// 获取到设备历史数据数量
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice dataCount: Int, packageCount: Int, peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDevice data: [Int], packageNumber: Int, peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceDataError peripheral: CBPeripheral)
    /// 获取到设备历史数据包
    func bleDevice(_ bleDevice: SSBleDevice, didGetDeviceEnd peripheral: CBPeripheral)
}
// Optional
extension SSBleDeviceDelegate {
    
}
protocol SSBleDeviceScanDelegate: AnyObject {
    /// 扫描到蓝牙设备
    func bleDevice(_ bleDevice: SSBleDevice, didDiscover peripheral: CBPeripheral, mac:String, rssi RSSI: NSNumber)
}
class SSBleDevice: NSObject {
    
    /// 服务标识
    private let kServiceUUID = "8653000A-43E6-47B7-9CB0-5FC21D4AE340"
    /// 写服务标识
    private let kWriteUUID = "8653000C-43E6-47B7-9CB0-5FC21D4AE340"
    /// 接收通知标识
    private let kNotiUUID = "8653000B-43E6-47B7-9CB0-5FC21D4AE340"
    /// 代理
    weak var delegate: SSBleDeviceDelegate?
    /// 扫描代理
    weak var scanDelegate: SSBleDeviceScanDelegate?
    /// 蓝牙状态
    public var state: SSBleState = .poweredOff
    
    /// 蓝牙管理者
    private var centralManager: CBCentralManager?
    /// 蓝牙设备
    public var peripherals: [CBPeripheral] = []
    public var writeChars: [String: CBCharacteristic] = [:]
    public var peripheralTokens: [String: Data] = [:]
    /// 初始化
    convenience init(delegate: SSBleDeviceDelegate?, identifier: String? = nil) {
        
        self.init()
        if let identifier = identifier {
            centralManager = CBCentralManager(delegate: self, queue: DispatchQueue(label: "La.ble", attributes: .concurrent), options: [CBCentralManagerOptionRestoreIdentifierKey:identifier,CBCentralManagerOptionShowPowerAlertKey:true])
        }else {
            centralManager = CBCentralManager(delegate: self, queue: DispatchQueue(label: "La.ble", attributes: .concurrent))
        }
        self.delegate = delegate
    }
    
    override init() {
        super.init()
    }
    
    deinit {
        stopScan()
    }
    
    // 根据uuid取到外设
    func retrievePeripheralWithUUID(uuidStrings:[UUID]) -> Bool {
        if let peripherals = centralManager?.retrievePeripherals(withIdentifiers: uuidStrings) {
            self.peripherals.append(contentsOf: peripherals)
            for per in peripherals {
                centralManager?.connect(per, options: nil)
            }
            return true
        }
        return false
    }
    
    /// 开始扫描
    public func startScan() {
        centralManager?.stopScan()
        centralManager?.scanForPeripherals(withServices: nil, options: nil)
    }
    
    /// 停止扫描
    public func stopScan() {
        centralManager?.stopScan()
    }
    
    /// 连接设备
    public func connect(peripheral: CBPeripheral) {
        guard peripheral.state != .connected else {
            return
        }
        SSLog("开始连接" + (peripheral.name ?? "unknown"))
        centralManager?.connect(peripheral, options: nil)
    }
    
    /// 断开设备
    public func cancelConnect(peripheral: CBPeripheral) {
        guard peripheral.state == .disconnected else {
            return
        }
        centralManager?.cancelPeripheralConnection(peripheral)
    }
    
    /// 发送数据
    func sendKeyValue(peripheral: CBPeripheral, data: Data, char: CBCharacteristic) {
        SSLog("发送数据:\(dataToString(data: data))")
        /// 向写服务里面写数据
        peripheral.writeValue(data, for: char, type: .withoutResponse)
    }
    
    /// Data 转 String
    public func dataToString(data: Data) -> String {
        var str = "0x"
        for i in data {
            str += String(format: "%02X ", i&0xff)
        }
        return str
    }
    /// Data 转 String
    public func dataString(data: Data) -> String {
        var str = ""
        for i in data {
            str += String(format: "%02X", i&0xff)
        }
        return str
    }
}
extension SSBleDevice: CBCentralManagerDelegate {
    
    /// 手机蓝牙状态更新
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            SSCommon.saveLog(tag: "Ble", info: "手机蓝牙 开")
            state = .poweredOn
        case .poweredOff:
            SSCommon.saveLog(tag: "Ble", info: "手机蓝牙 关")
            state = .poweredOff
        default:
            state = .poweredOff
        }
        delegate?.bleDeviceDidUpdateState(self)
    }
    
    /// 后台被杀死后重启连接 （但是后台重连后，设备连接着，但没有状态返回，无法显示控制界面，所以通过断开重连的方式来显示界面）
    func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
        
        SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 设备 \(dict)")
        if let array:Array = dict[CBCentralManagerRestoredStatePeripheralsKey] as? Array<CBPeripheral>, array.count > 0 {
            
            peripherals.removeAll()
            if let peripheral:CBPeripheral = array.first {
                if(peripheral.state == CBPeripheralState.connected) {
                    SSLog("restore connect \(peripheral.identifier)");
                    for service in peripheral.services! {
                        if service.uuid.isEqual(CBUUID(string: kServiceUUID)) {
                            for characteristic in service.characteristics! {
                                if characteristic.uuid.isEqual(CBUUID(string: kWriteUUID)) {
                                    // 打开写入服务
                                    if let name = peripheral.name {
                                        writeChars[name] = characteristic
                                    }
                                }
                            }
                        }
                    }
                    // 立即重连后无法断开
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 主动断开")
                        self.centralManager?.cancelPeripheralConnection(peripheral)
                        // 断开后要等几秒钟，低于两秒连不上
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            SSCommon.saveLog(tag: "Ble", info: "尝试恢复连接 再次重连")
                            self.centralManager?.connect(peripheral, options: nil)
                        }
                    }
                    delegate?.bleDeviceWillRestoreState(self)
                    return;
                }
                centralManager?.connect(peripheral, options: nil)
            }
        }
    }
    
    /// 扫描到的蓝牙设备
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        
        guard let name = advertisementData["kCBAdvDataLocalName"] as? String else {
            return
        }
        SSLog("发现设备" + name)
        guard (name.hasPrefix("BD_CMG") || name.hasPrefix("BECGM")) else {
            return
        }
        SSCommon.saveLog(tag: "Ble", info: "发现 设备 \(String(describing: peripheral.name)) local name \(name)")
        scanDelegate?.bleDevice(self, didDiscover: peripheral, mac: "", rssi: RSSI)
    }
    
    /// 蓝牙设备连接上
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        // 服务发现并打开之后，我们才认为连接成功
        SSLog("连接成功" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接成功 设备 \(String(describing: peripheral.name))")
        peripherals.append(peripheral)
        peripheral.delegate = self
        peripheral.discoverServices(nil)
        delegate?.bleDevice(self, didConnect: peripheral)
    }
    
    /// 蓝牙连接失败
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        SSLog("连接失败" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接失败 设备 \(String(describing: peripheral.name))")
        peripherals.removeAll { (per) -> Bool in
            per.identifier == peripheral.identifier
        }
        delegate?.bleDevice(self, didFailToConnect: peripheral, error: error)
    }
    
    /// 蓝牙设备断开
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        SSLog("连接断开" + (peripheral.name ?? "unknown"))
        SSCommon.saveLog(tag: "Ble", info: "连接断开 设备 \(String(describing: peripheral.name))")
        peripherals.removeAll { (per) -> Bool in
            per.identifier == peripheral.identifier
        }
        delegate?.bleDevice(self, didDisconnectPeripheral: peripheral)
    }
}
extension SSBleDevice: CBPeripheralDelegate {
    
    /// 1、发现服务
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        
        for service in peripheral.services! {
            if service.uuid.isEqual(CBUUID(string: kServiceUUID)) {
                // 去发现特征
                peripheral.discoverCharacteristics(nil, for: service)
                break
            }
        }
    }
    
    /// 2、发现服务的特征值
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        
        for characteristic in service.characteristics! {
            if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
                // 打开通知服务
                peripheral.setNotifyValue(true, for: characteristic)
            } else if characteristic.uuid.isEqual(CBUUID(string: kWriteUUID)) {
                // 打开写入服务
                peripheral.setNotifyValue(true, for: characteristic)
                if let name = peripheral.name {
                    writeChars[name] = characteristic
                }
            }
        }
    }
    
    /// 3、确认通知服务打开，读取外设实时数据
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        
        if characteristic.isNotifying {
        
            if peripherals.filter({$0.identifier == peripheral.identifier}).count == 0 {
                peripherals.append(peripheral)
            }
            //TODO: 确认通知服务打开，再加上身份验证，才确认连接成功
            if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
                if let name = peripheral.name {
//                    writeIdentity(per: peripheral, name: name)
//                    checkCurrentData(per: peripheral, name: name)
                    delegate?.bleDevice(self, didConnect: peripheral)
                }
            }
        }
    }
    
    /// 4、接收到数据
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        
        if characteristic.uuid.isEqual(CBUUID(string: kNotiUUID)) {
            guard let data = characteristic.value else {
                return
            }
            SSLog("接收数据 【\(peripheral.name ?? "unknown")】：\(dataToString(data: data))")
            transData(peripheral: peripheral, data: data)
        }
    }
}
//
//  SSBleDeivce+TransData.swift
//  BeltConsumer
//
//
import Foundation
import CoreBluetooth
// MARK: - 接收数据包处理
extension SSBleDevice {
    
    /// 蓝牙数据处理
    func transData(peripheral: CBPeripheral, data: Data) {
        // 视图在蓝牙收到的数据里进行判断显示，如果是收到的数据是正在工作，则将收到的数据的工作时间换算成开始时间，将数据模型重新更新
            
        SSCommon.saveLog(tag: "Ble", info: "接收数据 \(String(describing: peripheral.name)) \(dataToString(data: data))")
        if data[0] == 0x01 {
        } else if data[0] == 0x16 { // token
//            // 身份验证信息正确才确认连接成功
//            if data[1] == 0x5B && data[2] == 0x01 {
//                if let name = peripheral.name {
//                    self.peripheralTokens[name] = Data([data[4], data[5], data[6], data[7]])
//                    delegate?.bleDevice(self, didConnect: peripheral)
//                }
//            }
        } else if data[0] == 0x8A, data[1] == 0xA8 {
            if data[2] == 0x02 { // 实时数据
                // 工作时间
                let dataString = data.ss_dataToString()
                let timeStartIndex0 = dataString.index(dataString.startIndex, offsetBy: 8)
                let timeEndIndex0 = dataString.index(dataString.startIndex, offsetBy: 11)
                let deviceData0 = String(dataString[timeStartIndex0...timeEndIndex0]).ss_hexStringToInt()
                
                let timeStartIndex1 = dataString.index(dataString.startIndex, offsetBy: 12)
                let timeEndIndex1 = dataString.index(dataString.startIndex, offsetBy: 15)
                let deviceData1 = String(dataString[timeStartIndex1...timeEndIndex1]).ss_hexStringToInt()
                
                
                let timeStartIndex2 = dataString.index(dataString.startIndex, offsetBy: 16)
                let timeEndIndex2 = dataString.index(dataString.startIndex, offsetBy: 19)
                let deviceData2 = String(dataString[timeStartIndex2...timeEndIndex2]).ss_hexStringToInt()
                
                
                let timeStartIndex3 = dataString.index(dataString.startIndex, offsetBy: 20)
                let timeEndIndex3 = dataString.index(dataString.startIndex, offsetBy: 23)
                let deviceData3 = String(dataString[timeStartIndex3...timeEndIndex3]).ss_hexStringToInt()
                
                SSLog("实时数据：\(deviceData0):\(deviceData1):\(deviceData2):\(deviceData3)")
                SSCommon.saveLog(tag: "Ble", info: "实时数据")
                delegate?.bleDevice(self, didGetDeviceCurrentData: "\(deviceData0):\(deviceData1):\(deviceData2):\(deviceData3)", peripheral: peripheral)
                
            } else if data[2] == 0x03 { // 历史数据数量
                let isHasHistory = Int(data[4])
                let laDataCount = Int(data[5]) * 256 + Int(data[6])
                let laPackageCount = Int(data[7]) * 256 + Int(data[8])
                SSLog(isHasHistory)
                SSLog(laDataCount)
                SSLog(laPackageCount)
                SSCommon.saveLog(tag: "Ble", info: "历史数据数量")
                if isHasHistory == 1 {
                    delegate?.bleDevice(self, didGetDevice: laDataCount, packageCount: laPackageCount, peripheral: peripheral)
                }
            } else if data[2] == 0x04 { // 历史数据数量
                
                let index = Int(data[3]) * 256 + Int(data[4])
                let dataCount = Int(data[5]) * 256 + Int(data[6])
                SSLog(index)
                SSLog(dataCount)
                SSCommon.saveLog(tag: "Ble", info: "历史数据\(String(describing: peripheral.name)) 序号\(index) 数量\(dataCount)")
                let maxCount = (dataCount-1)*2 + 8 + 1
                let currentCount = data.count
                guard currentCount >= maxCount  else {
                    SSHUD.ss_showError(withStatus: "数据长度错误, 数据序号:\(index)大小:\(dataCount)个\n数据内容：\(dataToString(data: data))")
                    SSCommon.saveLog(tag: "Ble", info: "数据有问题")
                    delegate?.bleDevice(self, didGetDeviceDataError: peripheral)
                    return
                }
                
                var dataArr = [Int]()
                for index in 0..<dataCount {
                    let data = Int(data[index*2 + 7]) * 256 + Int(data[index*2 + 8])
                    dataArr.append(data)
                }
                
                delegate?.bleDevice(self, didGetDevice: dataArr, packageNumber: index, peripheral: peripheral)
            } else if data[2] == 0x05 { // 历史数据数量
                SSCommon.saveLog(tag: "Ble", info: "结束历史数据")
                delegate?.bleDevice(self, didGetDeviceEnd: peripheral)
            }
        }
    }
}
//
//  SSBleDevice+Write.swift
//  BeltConsumer
//
//
import Foundation
import CoreBluetooth
/// 发送数据包处理
extension SSBleDevice {
    
    /// 身份验证
    func writeIdentity(per:CBPeripheral, name: String) {
        
        guard let char = writeChars[name] else {
            return
        }
        let byte: [UInt8] = [0x18, 0x5A, 0x01, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]
        self.sendKeyValue(peripheral: per, data: Data(byte), char: char)
    }
    
    func checkCurrentData(per:CBPeripheral,name: String) {
        guard
//            let token = peripheralTokens[name],
              let char = writeChars[name] else {
            return
        }
        let byte: [UInt8] = [0x8A, 0xA8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.sendKeyValue(peripheral: per, data: Data(byte), char: char)
    }
    
    func checkHistoryData(per:CBPeripheral,name: String) {
        
        guard
//            let token = peripheralTokens[name],
              let char = writeChars[name] else {
            return
        }
        let byte: [UInt8] = [0x8A, 0xA8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.sendKeyValue(peripheral: per, data: Data(byte), char: char)
    }
    
    func checkPackageData(per:CBPeripheral,name: String, package: Int) {
        guard
//            let token = peripheralTokens[name],
              let char = writeChars[name] else {
            return
        }
        let byte: [UInt8] = [0x8A, 0xA8, 0x04, UInt8((package & 0xff00) >> 8), UInt8(package & 0xff), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.sendKeyValue(peripheral: per, data: Data(byte), char: char)
    }
    
    func checkHistoryEndData(per:CBPeripheral,name: String) {
        guard
//            let token = peripheralTokens[name],
              let char = writeChars[name] else {
            return
        }
        let byte: [UInt8] = [0x8A, 0xA8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.sendKeyValue(peripheral: per, data: Data(byte), char: char)
    }
}
//
//  memoryMacro.swift
//  DocPro
//
//
import Foundation
// TODO: - 存储
/// 版本号
let kLocalBindDeviceNames = "kLocalBindDeviceNames"
//
//  networkMacro.swift
//  DocPro
//
//
import Foundation
/// 网络baseUrl
let kBaseUrl = "https://app.bioecare.com/admin/bloodSugarApi/"
/// 登录
let kLoginUrl = kBaseUrl + "getToken"
/// 获取绑定的设备
let kGetDevices = kBaseUrl + "bioPhones"
import UIKit
func SSLog<T>(_ items: T, file: String = #file, lineNumber: Int = #line, fuc: String = #function) {
    
    #if DEBUG
        let fileName = (file as NSString).lastPathComponent
        let time = Date.ss_todayStr()
        print("=-=-=-=-= \(time) [\(fileName)] [\(fuc)] line:\(lineNumber) =-=-=-=-= \n\(items)")
    #endif
}
// 颜色
let kThemeColor = UIColor.ss_colorWithHexString(hexStr: "#8363FB")
let kThemeLightColor = UIColor.ss_colorWithHexString(hexStr: "#A48BFF")
let kThemeYellowColor = UIColor.ss_colorWithHexString(hexStr: "#FFAB67")
let kThemeShadowColor = UIColor.ss_colorWithHexString(hexStr: "#9F85FF")
let kThemeGrayShadowColor = UIColor.ss_colorWithHexString(hexStr: "#F1EDFF")
let kThemeGrayColor = UIColor.ss_colorWithHexString(hexStr: "#F5F5F5")
let kBgColor = UIColor.ss_colorWithHexString(hexStr: "#f1f2f3")
let kLineColor = UIColor.ss_colorWithHexString(hexStr: "#d1d1d1")
let kBlueColor = UIColor.ss_colorWithHex(rgbValue: 0x24bcd7)
let kYellowColor = UIColor.ss_colorWithHex(rgbValue: 0xfdb667)
let kBlackColor = UIColor.ss_colorWithHex(rgbValue: 0x00214f)
let kGrayColor = UIColor.ss_colorWithHex(rgbValue: 0x8d8d8d)
let gradientColors = [UIColor.ss_colorWithHexString(hexStr: "34d7de").cgColor,
                      UIColor.ss_colorWithHexString(hexStr: "3fc89d").cgColor]
// 屏幕尺寸
let kScreenWidth = UIScreen.main.bounds.width
let kScreenHeight = UIScreen.main.bounds.height
let kScreenScale = UIScreen.main.scale // 屏幕分辨率
let kScreenRatio =  (kScreenWidth >= 415) ? 1.15 : ((kScreenWidth >= 375) ? 1 : 0.85) // 屏幕适配
/// 默认模式
let kDefaultModeTitle = ["放松", "防萎缩", "加强"]
/// 普通模式编号
let kLowerCustomMode = 4
/// 测试设备名
let kDefaultDeviceName = "ECH-d1-"
/// 调整设备的延时开始时间
let kTimerBeginStart = 3.4
// TODO: - 通知
/// 退出登录
let kNotificationLogout = "kNotificationLogout"
/// 注册和登录通知
let kNotificationRegisterAlias = "kNotificationRegisterAlias"
/// 通知收到新方案
let kNotificationReciveNewPlan = "kNotificationReciveNewPlan"
/// 通知收到新消息
let kNotificationReciveNewMessage = "kNotificationReciveNewMessage"
/// 点击通知收到新消息
let kNotificationReciveNewMessageDidClick = "kNotificationReciveNewMessageDidClick"
/// 开始升级
let kNotificationUpdateStart = "kNotificationUpdateStart"
/// 升级成功
let kNotificationUpdateSuccess = "kNotificationUpdateSuccess"
/// 升级结束
let kNotificationUpdateFail = "kNotificationUpdateFail"
//
//  JSDBManager.swift
//  ECHTempPad
//
//
import Foundation
import WCDBSwift
enum DBTableName: String {
    case data = "LA_data"
    case device = "Device"
}
class SSDBManager {
    
    static let shared = SSDBManager()
    static let dbFileURL = URL(fileURLWithPath: NSHomeDirectory() + "/Documents/ECH_LA.db")
    init() {
        let db = Database(withFileURL: SSDBManager.dbFileURL)
        do {
            try db.create(table: DBTableName.data.rawValue, of: La_data.self)
            try db.create(table: DBTableName.device.rawValue, of: Device.self)
        } catch {
            SSLog(error)
        }
    }
}
//
//  JSDBManager+Extensions.swift
//  ECHTempPad
//
import Foundation
import WCDBSwift
extension SSDBManager {
    
    func getAllDevices() -> [Device] {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        let all:[Device]? = try? wcdb.getObjects(fromTable: DBTableName.device.rawValue)
        return all ?? []
    }
    
    func updateAllDevices(devices: [Device]) {
        deleteAllDevices()
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.insertOrReplace(objects: devices, intoTable: DBTableName.device.rawValue)
    }
    
    func deleteAllDevices() {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.delete(fromTable: DBTableName.device.rawValue)
    }
}
// 用户登录
extension SSDBManager {
    
    // 区分是否正在上传
    func getUpdateDataUploading() -> [La_data] {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        let all:[La_data]? = try? wcdb.getObjects(fromTable: DBTableName.data.rawValue, where: La_data.Properties.uploadStatus == 1, orderBy: [La_data.Properties.date.asOrder(by: .descending)])
        return all ?? []
    }
    
    // 区分是否正在上传
    func getAllDeviceDataNotUploaded() -> [La_data] {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        let all:[La_data]? = try? wcdb.getObjects(fromTable: DBTableName.data.rawValue, where: La_data.Properties.uploadStatus == 2, orderBy: [La_data.Properties.date.asOrder(by: .descending)], limit: 200)
        return all ?? []
    }
    
    func insertData(data: La_data) {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.insertOrReplace(objects: data, intoTable: DBTableName.data.rawValue)
    }
    func updateDataUploaded(data: La_data) {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.insertOrReplace(objects: data, intoTable: DBTableName.data.rawValue)
    }
    func insertHistoryData(data: [La_data]) {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.insertOrReplace(objects: data, intoTable: DBTableName.data.rawValue)
    }
    
    func updateHistoryDataUploaded(data: [La_data]) {
        guard !data.isEmpty else {
            return
        }
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.insertOrReplace(objects: data, intoTable: DBTableName.data.rawValue)
    }
    
    func getDeviceData(name: String) -> [La_data] {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        let all:[La_data]? = try? wcdb.getObjects(fromTable: DBTableName.data.rawValue, where: La_data.Properties.name == name, orderBy: [La_data.Properties.date.asOrder(by: .descending)], limit: 15000)
        return all ?? []
    }
    
    func getAllDeviceData() -> [La_data] {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        let all:[La_data]? = try? wcdb.getObjects(fromTable: DBTableName.data.rawValue, orderBy: [La_data.Properties.date.asOrder(by: .descending)], limit: 15000)
        return all ?? []
    }
    
    func deleteDivceDatas(name: String) {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.delete(fromTable: DBTableName.data.rawValue, where: La_data.Properties.name == name)
    }
    
    func clearAll() {
        let wcdb = Database(withFileURL: SSDBManager.dbFileURL)
        try? wcdb.delete(fromTable: DBTableName.data.rawValue)
    }
}
import Foundation
import UIKit
/// 发送验证码类型 0 用户注册 1用户找回密码
enum smsType: Int {
    case useRegister = 0
    case useForget = 1
}
// TODO: - 引导
extension SSNetworkManager {
}
// TODO: - 首页
extension SSNetworkManager {
}
extension SSNetworkManager {
}
extension SSNetworkManager {
}
extension SSNetworkManager {
}
import Foundation
typealias completion<T> = (Result<SSResponse<T?>, SSError>) -> Void
struct SSResponse<T> {
    
    let originalData: Any
    let parseData: T?
}
struct SSError: Error {
    let code: SSErrorCode
    let description: String
}
enum SSErrorCode:Int {
    
    case code_unknwon = 0
    case code_200 = 200
}
import UIKit
import Alamofire
let headers: HTTPHeaders = ["Content-type":"application/x-www-form-urlencoded",
                            "Accept":"application/json",
                            "systemtype":"ios",
                            "channel":"00",
                            "Authorization":""]
enum MethodType {
    case get
    case post
}
enum RequestType {
    case url
    case json
}
/// 网络管理工具
class SSNetworkManager: NSObject {
    
    /// 网络请求单例
    static let shared = SSNetworkManager()
    /// sessionManager变量
    static let sharedSessionManager: Alamofire.Session = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 30 // 超时要在这里设置
        return Alamofire.Session(configuration: configuration)
    }()
    
    /// 对Alamfire封装
    private func request(_ type: MethodType, URLString: String, parameters: [String: Any]? = nil, requestType: RequestType = .url, success:  @escaping(_ json: Any) -> () ,fail: @escaping(_ failDescription: String,_ statusCode:Int) -> ()) -> DataRequest {
//        SSLog("-----NetRequest------url:\(URLString)--parameters:\(String(describing: parameters))")
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        let method = type == .get ? HTTPMethod.get : HTTPMethod.post
        return SSNetworkManager.sharedSessionManager.request(URLString,method:method,parameters:parameters,encoding: requestType == .url ? URLEncoding.default : JSONEncoding.default)
            .validate(statusCode: 200..<300) // 验证返回状态，如果不在范围内，返回FAILURE
//            .validate(contentType: ["application/json"])
            .responseJSON { (response) in
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
                switch response.result {
                    case .success(let JSON):
                        success(JSON)
                    case .failure(let error):
                        if error.localizedDescription != "cancelled" {
                            fail(error.localizedDescription, error._code)
                        }
                }
        }
    }
    
    /// post请求
    ///
    /// - Parameters:
    ///   - URLString: 地址
    ///   - parameters: 参数
    ///   - isHideHud: 是否隐藏hud
    ///   - succeed: 成功的响应信息
    ///   - failure: 请求错误
    func postRequest(URLString: String, parameters: [String: Any]?, requestType: RequestType = .url, success: @escaping (_ dict: [String: Any]) -> (), failure: @escaping (_ failDescription: String, _ statusCode:Int) -> ()) -> DataRequest {
        return request(MethodType.post, URLString: URLString, parameters: parameters, requestType: requestType, success: { (anyJson) in
                        
            guard let dict = anyJson as? [String: Any],
                let status = dict["code"] as? Int,
                let message = dict["msg"] as? String
                else {
                    SSLog("服务器reponse格式错误")
                    failure("Server response error, please contact us", 0)
                    return
            }
            if status == 200 || status == 0 {
                success(dict)
            } else {
                SSLog("---- status: \(status) ---- message: \(message)")
                if status == 502 { // token 失效
                    SSHUD.ss_showError(withStatus: "登录失效，请尝试重新登录")
                    UserService.shared.logout()
                } else {
                    failure(message, status)
                }
                return
            }
            
        }) {error, code  in
            failure(error, code)
        }
    }
    
    /// 上传图片
    ///
    /// - Parameters:
    ///   - image: 需要上传的UIImage
    ///   - completion: 上传成功的url地址
    func uploadFile(image: UIImage, url: String, completion: @escaping (_ fileUrl: String) -> (), failure: @escaping (_ fileUrl: String) -> ()) {
        
        // 压缩图片
        guard let scaleImage = image.ss_scaleImage(maxLength: 500) else {
            failure("图片处理失败")
            return
        }
        // 图片转Data
        guard let data = scaleImage.jpegData(compressionQuality: 1) else {
            failure("图片处理失败")
            return
        }
        
        // 文件不能重名，用时间戳命名
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMDDHHmmss"
        let fileName = formatter.string(from: Date()) + ".jpg"
        
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        
        let manager = Alamofire.Session.default
        manager.session.configuration.timeoutIntervalForRequest = 30
        manager.upload(multipartFormData: { (formdata) in
            formdata.append(data, withName: "file", fileName: fileName, mimeType: "image/jpeg")
        }, to: url).uploadProgress(queue: .main, closure: { progress in
            print("Upload Progress: \(progress.fractionCompleted)")
        }).responseJSON(completionHandler: { response in
            print("upload finished: \(data)")
            switch response.result {
            case .success(let dataObj):
                print("upload success result: \(String(describing: dataObj))")
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
                
                guard let dict = dataObj as? [String: Any],
                    let status = dict["status"] as? Int,
                    let message = dict["message"] as? String,
                    let fileUrl = dict["fileUrl"] as? String
                    else {
                        failure("图片上传失败")
                        return
                }
                if status != 200 {
                    SSLog("---- status: \(status) ---- message: \(message)")
                    failure("图片上传失败")
                    return
                }
                //成功返回 fileUrl
                completion(fileUrl)
                
            case .failure(let err):
                failure("\(err.localizedDescription)")
            }
        })
    }
}
mport UIKit
extension String {
    
    /// 判断文本是否全是空格
    func ss_isAllSpace() -> Bool {
        
        let str = self.replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
        
        return (str.count == 0 ? true : false)
    }
    
    /// 移除文本中的 emoji
    func ss_emojiRemove() -> String {
        
        let regex = try!NSRegularExpression.init(pattern: "[^\\u0020-\\u007E\\u00A0-\\u00BE\\u2E80-\\uA4CF\\uF900-\\uFAFF\\uFE30-\\uFE4F\\uFF00-\\uFFEF\\u0080-\\u009F\\u2000-\\u201f\r\n]", options: .caseInsensitive)
        return regex.stringByReplacingMatches(in: self, options: [], range: NSMakeRange(0, self.count), withTemplate: "")
    }
    
    /// 判断是否为手机号
    func ss_isPhone() -> Bool {
        
        let regex = "1([0-9]){10}"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with: self)
    }
    
    /// 判断单个字符串是否是数字
    func ss_oneNumber() -> Bool {
        
        if self.count != 1 {
            return false
        }
        
        let regex = "[0-9]"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with: self)
    }
    /// 判断单个字符串是否是年龄
    func ss_age() -> Bool {
        
        let regex = "^[1-9][0-9]+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with: self)
    }
    
    func ss_name() -> Bool {
        
        let regex = "^[a-zA-Z0-9_.\\u4e00-\\u9fa5]+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with: self)
    }
    func ss_exceptEmoji() -> Bool {
        // 注意\\转义
        let regex = "^[a-zA-Z0-9\r\n\\s\\[\\]!@#$%^&*()_+-={}\\|:\"'`;<>?,./~·！￥…（）{}、【】：“‘；《》，。？、\\u4e00-\\u9fa5]+$"
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        return predicate.evaluate(with: self)
    }
}
extension String {
    
    /// MD5 加密 需在桥接文件中引入 #import <CommonCrypto/CommonDigest.h>
    ///
    /// - Returns: 32 位大写
//    func ss_MD5(_ isUpper: Bool = true) -> String {
//
//        let str = self.cString(using: .utf8)
//        let strLen = CUnsignedInt(self.lengthOfBytes(using: .utf8))
//        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
//        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity:digestLen)
//        CC_MD5(str!, strLen, result)
//
//        let hash = NSMutableString()
//        for i in 0 ..< digestLen {
//
//            if isUpper {
//
//                hash.appendFormat("%02X", result[i])
//            } else {
//
//                hash.appendFormat("%02x", result[i])
//            }
//        }
//        result.deinitialize(count: digestLen)
////        result.deinitialize()
//
//        return String(format: hash as String)
//    }
    
    func ss_hexStringToInt() -> Int {
        
        if self.count < 1 {
            return 0
        }
        
        let str = self.uppercased()
        var sum = 0
        for i in str.utf8 {
            sum = sum * 16 + Int(i) - 48 // 0-9 从48开始
            if i >= 65 {                 // A-Z 从65开始，但有初始值10，所以应该是减去55
                sum -= 7
            }
        }
        return sum
    }
}
extension String {
    
    /// 根据文字的宽度和字体返回 高度
    ///
    /// - Parameters:
    ///   - width: 文字宽度
    ///   - font: 文字字体
    /// - Returns: 文字高度
    func ss_getStrHeight(width: CGFloat, font: UIFont) -> CGFloat {
        
        let size = CGSize(width: width, height: CGFloat(MAXFLOAT))
        let strSize = (self as NSString?)?.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size
        
        return strSize?.height ?? 0
    }
    
    /// 根据文字的高度和字体返回 宽度
    ///
    /// - Parameters:
    ///   - width: 文字高度
    ///   - font: 文字字体
    /// - Returns: 文字宽度
    func ss_getStrWidth(height: CGFloat, font: UIFont) -> CGFloat {
        
        let size = CGSize(width: CGFloat(MAXFLOAT), height: height)
        let strSize = (self as NSString?)?.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size
        
        return strSize?.width ?? 0
    }
}
extension String {
    
    /*
     CIImage
     带便图像的对象
     CIFilter
     表示滤镜，使用key-value coding设置输入值，滤镜强度和输入的CIImage 包含了一个对输入图像的引用以及需要应用于数据的滤镜
     CIContext
     用于渲染CIImage，当一个图片被渲染，作用于图片的滤镜链会被应用到原始的图像数据上。 CIContext可以是基于CPU的，输出为CGImageRef，也可以是基与GPU的，开发者可通过Open ES 2.0 画出来
     CIDetector
     CIDetector用于分析CIImage，以得到CIFeature，每个CIDetector都要用一个探测器类型（NSString）来初始化。这个类型用于告诉探测器要找什么特征
     CIFeatureCIFaceFeature
     当一个CIDetector 分析一个图像，返回值是一个根据探测器类型探测到的CIFeature数组。每一个CIFaceFeature都包含了一个面部的CGRect引用，以及检测到的面孔的左眼、右眼、嘴部对应的CGpoint
     */
    
    
    /**
     通过字符串生成二维码
     
     - parameter size:    宽度大小
     - parameter color:   颜色
     - parameter bgColor: 背景颜色
     
     - returns: UIImage
     */
    func ss_generateImage(sizeWidth: CGFloat, color: UIColor = UIColor.black, bgColor: UIColor = UIColor.clear) -> UIImage {
        
        //2.二维码滤镜
        let contentData = self.data(using: String.Encoding.utf8)
        let fileter = CIFilter(name: "CIQRCodeGenerator")
        
        fileter?.setValue(contentData, forKey: "inputMessage")
        fileter?.setValue("H", forKey: "inputCorrectionLevel")
        
        let ciImage = fileter?.outputImage
        
        //3.颜色滤镜
        let colorFilter = CIFilter(name: "CIFalseColor")
        
        colorFilter?.setValue(ciImage, forKey: "inputImage")
        colorFilter?.setValue(CIColor(cgColor: color.cgColor), forKey: "inputColor0")// 二维码颜色
        colorFilter?.setValue(CIColor(cgColor: bgColor.cgColor), forKey: "inputColor1")// 背景色
        
        //4.生成处理
        let outImage = colorFilter!.outputImage
        let scale = sizeWidth / outImage!.extent.size.width;
        
        let transform = CGAffineTransform(scaleX: scale, y: scale)
        
        let transformImage = colorFilter!.outputImage!.transformed(by: transform)
        
        return UIImage(ciImage: transformImage)
        
    }
    
}
import UIKit
extension UILabel {
    
    /// 知道 label 的宽度，求高度
    ///
    /// - Returns: label的高度
    func ss_getLabelHeight() -> CGFloat {
        let size = CGSize(width: ss_width, height: CGFloat(MAXFLOAT))
        let strSize = (text as NSString?)?.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font as Any], context: nil).size
        
        return strSize?.height ?? 0
    }
    
    /// 知道 label 的宽度，求高度
    ///
    /// - Returns: label的高度
    func ss_getLabelWeight() -> CGFloat {
        let size = CGSize(width: CGFloat(MAXFLOAT), height: ss_height)
        let strSize = (text as NSString?)?.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font as Any], context: nil).size
        
        return strSize?.width ?? 0
    }
}
import UIKit
extension UIFont {
    func sizeOfString(string: String, constrainedToWidth width: Double, lineSpace: CGFloat = 0) -> CGSize {
        
        let paraph = NSMutableParagraphStyle()
        paraph.lineSpacing = lineSpace
        return NSString(string: string).boundingRect(with: CGSize(width: width, height: Double.greatestFiniteMagnitude),
                                                     options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                     attributes: [NSAttributedString.Key.font: self, NSAttributedString.Key.paragraphStyle: paraph],
                                                     context: nil).size
    }
}
import UIKit
extension UIBarButtonItem {
    
    /// 创建一个 UIBarButtonItem
    class func ss_barButtonItem(title: String? = nil, right: UIControl.ContentHorizontalAlignment = .right, titleColor: UIColor = UIColor.ss_colorWithHexString(hexStr: "333333"), image: UIImage? = nil, target: Any?, action: Selector) -> UIBarButtonItem {
        
        let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 80, height: 40))
        btn.setTitle(title, for: [])
        btn.contentHorizontalAlignment = right
        btn.titleLabel?.font = UIFont.systemFont(ofSize: 17)
        btn.setTitleColor(titleColor, for: [])
        btn.setImage(image, for: [])
        btn.addTarget(target, action: action, for: .touchUpInside)
        
        return UIBarButtonItem(customView: btn)
    }
}
import UIKit
extension UIImage {
    
    /// 创建一个可自由拉伸的 UIImage
    class func ss_resizeImage(imageName: String, scaleHeight: CGFloat = 0.5) -> UIImage {
        
        guard var image = UIImage(named: imageName) else {
            return UIImage()
        }
        
        image = image.stretchableImage(withLeftCapWidth: Int(image.size.width * 0.5), topCapHeight: Int(image.size.height * scaleHeight))
        
        return image
    }
    
    /// 圆角头像
    ///
    /// - Parameters:
    ///   - size: 圆角尺寸
    ///   - backColor: 背景颜色
    ///   - lineColor: 圆圈颜色
    /// - Returns: UIImage
    func ss_avatarImage(size: CGSize?, backColor: UIColor = UIColor.white, lineColor: UIColor = UIColor.lightGray) -> UIImage? {
        var size = size
        if size == nil {
            size = self.size
        }
        let rect = CGRect(origin: CGPoint(), size: size!)
        
        UIGraphicsBeginImageContextWithOptions(rect.size, true, 0)
        
        backColor.setFill()
        UIRectFill(rect)
        
        let path = UIBezierPath(ovalIn: rect)
        path.addClip()
        
        draw(in: rect)
        
        let ovalPath = UIBezierPath(ovalIn: rect)
        ovalPath.lineWidth = 2
        lineColor.setStroke()
        ovalPath.stroke()
        
        let result = UIGraphicsGetImageFromCurrentImageContext()
        
        UIGraphicsEndImageContext()
        
        return result
    }
}
extension UIImage {
    
    /// 将图片模糊化 这个方法模拟器特别慢。。。
    func ss_blurImage() -> UIImage {
        
        // 获取原始图片
        let inputImage = CIImage(image: self)
        // 使用高斯模糊
        let filter = CIFilter(name: "CIGaussianBlur")!
        filter.setValue(inputImage, forKey:kCIInputImageKey)
        //设置模糊半径值（越大越模糊）
        filter.setValue(10, forKey: kCIInputRadiusKey)
        let outputCIImage = filter.outputImage!
        let rect = CGRect(origin: CGPoint.zero, size: self.size)
        
        let context = CIContext()
        let cgImage = context.createCGImage(outputCIImage, from: rect)
        
        //显示生成的模糊图片
        return UIImage(cgImage: cgImage!)
    }
}
extension UIImage {
    
    /// 根据颜色生成一张纯色的图片
    class func ss_imageFromColor(color: UIColor, viewSize: CGSize) -> UIImage? {
    
        let rect: CGRect = CGRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)
        
        UIGraphicsBeginImageContext(rect.size)
        
        let context: CGContext = UIGraphicsGetCurrentContext()!
        
        context.setFillColor(color.cgColor)
        
        context.fill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        UIGraphicsGetCurrentContext()
        
        return image
    }
}
extension UIImage {
    
    
    func ss_scaleImage(maxLength: Int) -> UIImage? {
        let newSize = UIImage.scaleImage(image: self, imageLength: CGFloat(maxLength))
        let newImage = UIImage.resizeImage(image: self, newSize: newSize)
        return newImage
    }
    
    ///对指定图片进行拉伸
    func resizableImage(name: String) -> UIImage {
        
        var normal = UIImage(named: name)!
        let imageWidth = normal.size.width * 0.5
        let imageHeight = normal.size.height * 0.5
        normal = resizableImage(withCapInsets: UIEdgeInsets.init(top: imageHeight, left: imageWidth, bottom: imageHeight, right: imageWidth))
        
        return normal
    }
    
import UIKit

extension UIColor {
    
    /// 根据16进制返回颜色
    ///
    /// - Parameter rgbValue: 0x123456
    /// - Returns: UIColor
    class func ss_colorWithHex(rgbValue: Int) -> UIColor {
        
        return ss_colorWithRGB(red: (CGFloat)((rgbValue & 0xFF0000) >> 16), green: (CGFloat)((rgbValue & 0xFF00) >> 8), blue: (CGFloat)(rgbValue & 0xFF))
    }
    
    /// 根据16进制返回颜色
    ///
    /// - Parameter hexStr: "#xxxxxx"
    /// - Returns: UIColor
    class func ss_colorWithHexString(hexStr: String) -> UIColor {
        
        // 去除空格等
        var cString: String = hexStr.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines).uppercased()
        
        // 去除#
        if (cString.hasPrefix("#")) {
            cString = (cString as NSString).substring(from: 1)
        }
        // 必须为6位
        if (cString.count != 6) {
            return UIColor.black
        }
        // 取色值
        let rString = (cString as NSString).substring(to: 2)
        let gString = ((cString as NSString).substring(from: 2) as NSString).substring(to: 2)
        let bString = ((cString as NSString).substring(from: 4) as NSString).substring(to: 2)
        
        // 字符串转换
        var r:CUnsignedInt = 0, g:CUnsignedInt = 0, b:CUnsignedInt = 0
        Scanner(string: rString).scanHexInt32(&r)
        Scanner(string: gString).scanHexInt32(&g)
        Scanner(string: bString).scanHexInt32(&b)
        
        return ss_colorWithRGB(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b))
        
    }
    
    /// 创建随机颜色
    class func ss_randomColor() -> UIColor {
        
        return ss_colorWithRGB(red: CGFloat(arc4random_uniform(256)), green: CGFloat(arc4random_uniform(256)), blue: CGFloat(arc4random_uniform(256)))
    }
    
    /// 使用 R、G、B 三原色创建颜色，不用除255.0
    class func ss_colorWithRGB(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat = 1.0) -> UIColor {
        
        return UIColor(red: (red) / 255.0, green: (green) / 255.0, blue: (blue) / 255.0, alpha: (alpha))
    }
}
import UIKit

extension UIView {
    func makeGradiented(subview: UIView, colors:[CGColor]) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = subview.frame
        gradientLayer.colors = colors
        gradientLayer.startPoint = CGPoint(x: 0, y: 1)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        layer.addSublayer(gradientLayer)
        gradientLayer.mask = subview.layer
        subview.frame = gradientLayer.bounds
    }
}

extension UIView {
    
    /// 切圆角和边框
    ///
    /// - Parameters:
    ///   - cornerRadius: 圆角角度
    ///   - borderWidth: 边框宽度
    ///   - borderColor: 边框颜色
    func ss_extension(cornerRadius: CGFloat = 5, borderWidth: CGFloat = 1, borderColor: UIColor = UIColor.lightGray) {
        layer.cornerRadius = cornerRadius
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor
    }
    
    /// 切圆角和边框
    ///
    /// - Parameters:
    ///   - cornerRadius: 圆角角度
    ///   - borderWidth: 边框宽度
    ///   - borderColor: 边框颜色
    func ss_shadow(radius: CGFloat, opacity: Float, color: UIColor, offset:CGSize) {
        
        layer.shadowColor = color.cgColor
        layer.shadowOffset = offset
        layer.shadowOpacity = opacity
        layer.shadowRadius = radius
    }
    
    /// 界面截图
    func ss_snapshotImage() -> UIImage?{
        
        UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0)
        
        drawHierarchy(in: bounds, afterScreenUpdates: true)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        
        UIGraphicsEndImageContext()
        
        return image
    }
}

extension UIView {
    
    // x
    var ss_x : CGFloat {
        
        get {
            
            return frame.origin.x
        }
        
        set(newVal) {
            
            var tmpFrame : CGRect = frame
            tmpFrame.origin.x     = newVal
            frame                 = tmpFrame
        }
    }
    
    // y
    var ss_y : CGFloat {
        
        get {
            
            return frame.origin.y
        }
        
        set(newVal) {
            
            var tmpFrame : CGRect = frame
            tmpFrame.origin.y     = newVal
            frame                 = tmpFrame
        }
    }
    
    // height
    var ss_height : CGFloat {
        
        get {
            
            return frame.size.height
        }
        
        set(newVal) {
            
            var tmpFrame : CGRect = frame
            tmpFrame.size.height  = newVal
            frame                 = tmpFrame
        }
    }
    
    // width
    var ss_width : CGFloat {
        
        get {
            
            return frame.size.width
        }
        
        set(newVal) {
            
            var tmpFrame : CGRect = frame
            tmpFrame.size.width   = newVal
            frame                 = tmpFrame
        }
    }
    
    // left
    var ss_left : CGFloat {
        
        get {
            
            return ss_x
        }
        
        set(newVal) {
            
            ss_x = newVal
        }
    }
    
    // right
    var ss_right : CGFloat {
        
        get {
            
            return ss_x + ss_width
        }
        
        set(newVal) {
            
            ss_x = newVal - ss_width
        }
    }
    
    // top
    var ss_top : CGFloat {
        
        get {
            
            return ss_y
        }
        
        set(newVal) {
            
            ss_y = newVal
        }
    }
    
    // bottom
    var ss_bottom : CGFloat {
        
        get {
            
            return ss_y + ss_height
        }
        
        set(newVal) {
            
            ss_y = newVal - ss_height
        }
    }
    
    var ss_centerX : CGFloat {
        
        get {
            
            return center.x
        }
        
        set(newVal) {
            
            center = CGPoint(x: newVal, y: center.y)
        }
    }
    
    var ss_centerY : CGFloat {
        
        get {
            
            return center.y
        }
        
        set(newVal) {
            
            center = CGPoint(x: center.x, y: newVal)
        }
    }
    
    var ss_size : CGSize {
    
        get {
        
            return bounds.size
        }
        
        set(newVal) {
            
            var tmpFrame : CGRect = frame
            tmpFrame.size         = newVal
            frame                 = tmpFrame
        }
    }
}
import UIKit

extension UIViewController {
    class func current(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return current(base: nav.visibleViewController)
        }
        if let tab = base as? UITabBarController {
            return current(base: tab.selectedViewController)
        }
        if let presented = base?.presentedViewController {
            return current(base: presented)
        }
        return base
    }
}
import UIKit

/// 时间格式
enum SSDateFormatterStr: String {
    
    case toSecond = "yyyy-MM-dd HH:mm:ss"
    case toDay = "yyyy-MM-dd"
    case toYear = "yyyy"
}

//  指定格式化方式，只创建一次，效率高
private let dateFormatter = DateFormatter()
/// 默认到秒
private let defaultDateFormatStr = SSDateFormatterStr.toSecond.rawValue

extension Date {
    
    /// 指定格式的 Date 字符串，默认到秒
    func ss_str(dateFormatStr: String = defaultDateFormatStr) -> String {
        
        dateFormatter.dateFormat = dateFormatStr
        
        let str = dateFormatter.string(from: self)
        return str
    }
    
    // 在非class的类型上下文中，我们统一使用static来描述类型作用域
    /// 将日期字符串转成指定格式的 Date，默认到秒
    static func ss_date(dateStr: String, dateFormatStr: String = defaultDateFormatStr) -> Date? {
        
        //  指定格式化方式
        dateFormatter.dateFormat = dateFormatStr
        //  把字符串转成时间对象
        let date = dateFormatter.date(from: dateStr)
        
        return date
    }
    
    /// 判断当前时间状态
    var ss_sinaDateString: String {
        
        //是今年
        if isThisYear(createAtDate: self) {
            
            //  日历对象
            let currentCalendar = Calendar.current
            if currentCalendar.isDateInToday(self) {
                
                let timeinterVal: TimeInterval = abs(self.timeIntervalSinceNow)
                if timeinterVal < 60 {
                    
                    return "刚刚"
                } else if timeinterVal < 3600 {
                    
                    let result = timeinterVal / 60
                    return "\(Int(result))分钟前"
                } else {
                    
                    let result = timeinterVal / 3600
                    return "\(Int(result))小时前"
                }
            } else if currentCalendar.isDateInYesterday(self) {
                //  表示昨天
                dateFormatter.dateFormat = "昨天 HH:mm"
            } else {
                //  其它
                dateFormatter.dateFormat = "MM-dd HH:mm"
            }
        } else {
            //  不是今年
            dateFormatter.dateFormat = "yyyy-MM-dd"
        }
        
        return dateFormatter.string(from: self)
    }
    
    //  根据指定时间对象判断是否是今年
    private func isThisYear(createAtDate: Date) -> Bool {
        
        //  指定格式化方式
        dateFormatter.dateFormat = "yyyy"
        //  获取时间的年份
        let createAtYear = dateFormatter.string(from: createAtDate)
        //  获取当前时间的年份
        let currentDateYear = dateFormatter.string(from: Date())
        //  判断时间年份是否相同
        return createAtYear == currentDateYear
        
    }
}

// TODO: - 解析今天的日期
extension Date {
    
    /// 指定格式的今天的 Date 字符串，默认到秒
    ///
    /// - Parameter dateFormatStr: 日期格式：yyyy-MM-dd
    /// - Returns: Date 字符串
    static func ss_todayStr(dateFormatStr: String = defaultDateFormatStr) -> String {
        
        dateFormatter.dateFormat = dateFormatStr
        // 指定格式的日期字符串
        let dateStr = dateFormatter.string(from: Date())
        
        return dateStr
    }
    
    /// 指定格式的今天的 Date，默认到秒
    ///
    /// - Parameter dateFormatStr: 日期格式：yyyy-MM-dd
    /// - Returns: Date
    static func ss_todayDate(dateFormatStr: String = defaultDateFormatStr) -> Date? {
        
        dateFormatter.dateFormat = dateFormatStr
        // 指定格式的日期字符串
        let dateStr = ss_todayStr(dateFormatStr: dateFormatStr)
        // 指定格式的日期Date
        let date = dateFormatter.date(from: dateStr)
        return date
    }
}
import UIKit

extension UIDevice {
    
    /// 获取系统版本
    class func ss_systemVersion() -> String {
        
        return UIDevice.current.systemVersion
    }
    
    /// 获取手机机型
    class func ss_iphoneType() -> String {
        
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        
        switch identifier {
        case "iPod1,1":  return "iPodTouch1"
        case "iPod2,1":  return "iPodTouch2"
        case "iPod3,1":  return "iPodTouch3"
        case "iPod4,1":  return "iPodTouch4"
        case "iPod5,1":  return "iPodTouch5"
        case "iPod7,1":  return "iPodTouch6"
            
        case "iPhone3,1", "iPhone3,2", "iPhone3,3":  return "iPhone4"
        case "iPhone4,1":  return "iPhone4s"
        case "iPhone5,1":  return "iPhone5"
        case "iPhone5,2":  return "iPhone5"
        case "iPhone5,3", "iPhone5,4":  return "iPhone5c"
        case "iPhone6,1", "iPhone6,2":  return "iPhone5s"
        case "iPhone7,2":  return "iPhone6"
        case "iPhone7,1":  return "iPhone6p"
        case "iPhone8,1":  return "iPhone6s"
        case "iPhone8,2":  return "iPhone6sp"
        case "iPhone8,4":  return "iPhoneSE"
        case "iPhone9,1", "iPhone9,3":  return "iPhone7"
        case "iPhone9,2", "iPhone9,4":  return "iPhone7p"
        case "iPhone10,1", "iPhone10,4": return "iPhone8"
        case "iPhone10,2", "iPhone10,5": return "iPhone8p"
        case "iPhone10,3", "iPhone10,6": return "iPhoneX"
        case "iPhone11,8": return "iPhoneXR"
        case "iPhone11,2": return "iPhoneXS"
        case "iPhone11,6","iPhone11,4": return "iPhoneXSm"
        case "iPhone12,1": return "iPhone11"
        case "iPhone12,3": return "iPhone11p"
        case "iPhone12,5": return "iPhone11pm"
        case "iPhone12,8": return "iPhonese2"
        case "iPhone13,1": return "iPhone12m"
        case "iPhone13,2": return "iPhone12"
        case "iPhone13,3": return "iPhone12p"
        case "iPhone13,4": return "iPhone12pm"
        case "iPhone14,4": return "iPhone13m"
        case "iPhone14,5": return "iPhone13"
        case "iPhone14,2": return "iPhone13p"
        case "iPhone14,3": return "iPhone13pm"

        case "iPad1,1": return "iPad"
        case "iPad1,2": return "iPad 3G"
        case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":  return "iPad2"
        case "iPad3,1", "iPad3,2", "iPad3,3":  return "iPad3"
        case "iPad3,4", "iPad3,5", "iPad3,6":  return "iPad4"
        case "iPad6,11", "iPad6,12":  return "iPad5"
        case "iPad7,5", "iPad7,6":  return "iPad6"
        case "iPad7,11", "iPad7,12":  return "iPad7"
            
        case "iPad4,1", "iPad4,2", "iPad4,3":  return "iPadAir"
        case "iPad5,3", "iPad5,4":  return "iPadAir2"
        case "iPad11,3", "iPad11,4":  return "iPadAir3"

        case "iPad2,5", "iPad2,6", "iPad2,7":  return "iPadMini"
        case "iPad4,4", "iPad4,5", "iPad4,6":  return "iPadMini2"
        case "iPad4,7", "iPad4,8", "iPad4,9":  return "iPadMini3"
        case "iPad5,1", "iPad5,2":  return "iPadMini4"
        case "iPad11,1", "iPad11,2":  return "iPadMini5"

        case "iPad6,3", "iPad6,4":  return "iPadPro9.7"
        case "iPad6,7", "iPad6,8":  return "iPadPro12.9"
        case "iPad7,1", "iPad7,2":  return "iPadPro12.92"
        case "iPad7,3", "iPad7,4":  return "iPadPro10.5"
        case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":  return "iPadPro11"
        case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":  return "iPadPro12.93"
        case "iPad8,9", "iPad8,10":  return "iPadPro112"
        case "iPad8,11", "iPad8,12":  return "iPadPro12.94"

        case "AppleTV2,1":  return "AppleTV2"
        case "AppleTV3,1","AppleTV3,2":  return "AppleTV3"
        case "AppleTV5,3":  return "AppleTV4"
            
        case "i386", "x86_64":  return "Simulator"
            
        default:  return identifier
        }
    }
